@readme/markdown 15.9.3 → 15.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import type { Extension as FromMarkdownExtension } from 'mdast-util-from-markdown';
2
+ /** Upstream `mdxExpressionFromMarkdown`, overriding only the text-expression exit to keep `{...}` source in image alt. */
3
+ export declare function mdxishExpressionFromMarkdown(): FromMarkdownExtension;
@@ -1,6 +1,5 @@
1
1
  import type { Extension as FromMarkdownExtension } from 'mdast-util-from-markdown';
2
2
  import type { Extension } from 'micromark-util-types';
3
- import { mdxExpressionFromMarkdown } from 'mdast-util-mdx-expression';
4
3
  import { mdxjsEsmFromMarkdown } from 'mdast-util-mdxjs-esm';
5
4
  import { emptyTaskListItemFromMarkdown } from '../mdast-util/empty-task-list-item';
6
5
  import { gemojiFromMarkdown } from '../mdast-util/gemoji';
@@ -9,6 +8,7 @@ import { jsxTableFromMarkdown } from '../mdast-util/jsx-table';
9
8
  import { legacyVariableFromMarkdown } from '../mdast-util/legacy-variable';
10
9
  import { magicBlockFromMarkdown } from '../mdast-util/magic-block';
11
10
  import { mdxComponentFromMarkdown } from '../mdast-util/mdx-component';
11
+ import { mdxishExpressionFromMarkdown } from '../mdast-util/mdx-expression';
12
12
  import { gemoji } from './gemoji';
13
13
  import { htmlBlockComponent } from './html-block-component';
14
14
  import { jsxComment } from './jsx-comment';
@@ -50,12 +50,12 @@ declare const REGISTRY: {
50
50
  };
51
51
  mdxExpressionLenient: {
52
52
  syntax: typeof mdxExpressionLenient;
53
- fromMarkdown: typeof mdxExpressionFromMarkdown;
53
+ fromMarkdown: typeof mdxishExpressionFromMarkdown;
54
54
  expression: true;
55
55
  };
56
56
  mdxExpression: {
57
57
  syntax: () => Extension;
58
- fromMarkdown: typeof mdxExpressionFromMarkdown;
58
+ fromMarkdown: typeof mdxishExpressionFromMarkdown;
59
59
  expression: true;
60
60
  };
61
61
  mdxComponent: {
@@ -3,8 +3,9 @@
3
3
  * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
4
4
  *
5
5
  * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
6
- * an MDX surface, and `{user.*}` is the syntax authors use there.
6
+ * an MDX surface, and `{user.*}` is the syntax authors use there. Image alt is the exception, resolved
7
+ * in the same pass so a substituted value is never rescanned (RM-10865).
7
8
  */
8
- export declare function resolveAttributeVariables(value: string, user: Record<string, unknown>): string;
9
+ export declare function resolveAttributeVariables(value: string, user: Record<string, unknown>, isImageAlt?: boolean): string;
9
10
  /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
10
11
  export declare const isBareVariableReference: (source: string) => boolean;
package/dist/main.js CHANGED
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 8846
11426
+ /***/ 714
11427
11427
  (module, __webpack_exports__, __webpack_require__) {
11428
11428
 
11429
11429
  "use strict";
@@ -73071,6 +73071,26 @@ const mdxToHast = () => tree => {
73071
73071
  };
73072
73072
  /* harmony default export */ const mdx_to_hast = (mdxToHast);
73073
73073
 
73074
+ ;// ./lib/mdast-util/mdx-expression/index.ts
73075
+
73076
+ const upstream = mdxExpressionFromMarkdown();
73077
+ /**
73078
+ * An image's alt is its label flattened to text, which drops an expression's braces. Keep the
73079
+ * `{...}` source there so `{user.*}` still resolves in alt at render time (RM-10865).
73080
+ */
73081
+ function exitTextExpression(token) {
73082
+ upstream.exit.mdxTextExpression.call(this, token);
73083
+ if (!this.stack.some(node => node.type === 'image'))
73084
+ return;
73085
+ const { children } = this.stack[this.stack.length - 1];
73086
+ const { position } = children[children.length - 1];
73087
+ children[children.length - 1] = { type: 'text', value: this.sliceSerialize(token), position };
73088
+ }
73089
+ /** Upstream `mdxExpressionFromMarkdown`, overriding only the text-expression exit to keep `{...}` source in image alt. */
73090
+ function mdxishExpressionFromMarkdown() {
73091
+ return { ...upstream, exit: { ...upstream.exit, mdxTextExpression: exitTextExpression } };
73092
+ }
73093
+
73074
73094
  ;// ./lib/mdast-util/empty-task-list-item/index.ts
73075
73095
  /**
73076
73096
  * Normalizes list items that are written as only `[ ]` or `[x]` into GFM task
@@ -73303,6 +73323,16 @@ function exitlegacyVariableValue(token) {
73303
73323
  function exitlegacyVariable(token) {
73304
73324
  const ctx = legacy_variable_contextMap.get(token);
73305
73325
  const serialized = this.sliceSerialize(token);
73326
+ // An escaped `\<<name>>` (or `\<<name\>>`) is literal text, as in the legacy parser.
73327
+ if (serialized.startsWith('\\')) {
73328
+ const literal = serialized.slice(1).replace('\\>>', '>>');
73329
+ // An image's alt is flattened to a string, so keep the escape for render-time resolution to honor.
73330
+ const inImageAlt = this.stack.some(node => node.type === 'image');
73331
+ this.enter({ type: 'text', value: inImageAlt ? `\\${literal}` : literal }, token);
73332
+ this.exit(token);
73333
+ legacy_variable_contextMap.delete(token);
73334
+ return;
73335
+ }
73306
73336
  const variableName = serialized.startsWith('<<') && serialized.endsWith('>>')
73307
73337
  ? serialized.slice(2, -2)
73308
73338
  : ctx?.value ?? '';
@@ -74896,9 +74926,16 @@ const legacyVariableConstruct = {
74896
74926
  function syntax_tokenize(effects, ok, nok) {
74897
74927
  let hasValue = false;
74898
74928
  const start = (code) => {
74929
+ effects.enter('legacyVariable');
74930
+ // Claim `\<<name>>` before CommonMark's character escape drops the backslash, as the legacy parser did.
74931
+ if (code !== codes.backslash)
74932
+ return open(code);
74933
+ effects.consume(code); // \
74934
+ return open;
74935
+ };
74936
+ const open = (code) => {
74899
74937
  if (code !== codes.lessThan)
74900
74938
  return nok(code);
74901
- effects.enter('legacyVariable');
74902
74939
  effects.enter('legacyVariableMarkerStart');
74903
74940
  effects.consume(code); // <
74904
74941
  return open2;
@@ -74938,7 +74975,7 @@ function syntax_tokenize(effects, ok, nok) {
74938
74975
  }
74939
74976
  function legacyVariable() {
74940
74977
  return {
74941
- text: { [codes.lessThan]: legacyVariableConstruct },
74978
+ text: { [codes.backslash]: legacyVariableConstruct, [codes.lessThan]: legacyVariableConstruct },
74942
74979
  };
74943
74980
  }
74944
74981
 
@@ -77262,12 +77299,12 @@ const REGISTRY = {
77262
77299
  magicBlock: { syntax: magicBlock, fromMarkdown: magicBlockFromMarkdown },
77263
77300
  // Two `{` tokenizers, never registered together: the document parser takes text
77264
77301
  // only, the component-body re-parser needs flow too for multi-line expressions.
77265
- mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxExpressionFromMarkdown, expression: true },
77302
+ mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxishExpressionFromMarkdown, expression: true },
77266
77303
  // `allowEmpty` is the package default; passed explicitly because `mdx-jsx`
77267
77304
  // registers the same tokenizer with it off for attribute expressions.
77268
77305
  mdxExpression: {
77269
77306
  syntax: () => mdxExpression({ allowEmpty: true }),
77270
- fromMarkdown: mdxExpressionFromMarkdown,
77307
+ fromMarkdown: mdxishExpressionFromMarkdown,
77271
77308
  expression: true,
77272
77309
  },
77273
77310
  mdxComponent: { syntax: mdxComponent, fromMarkdown: mdxComponentFromMarkdown },
@@ -80867,6 +80904,7 @@ const splitHtmlWithNestedTables = (node, source) => {
80867
80904
 
80868
80905
 
80869
80906
 
80907
+
80870
80908
 
80871
80909
 
80872
80910
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
@@ -80887,9 +80925,13 @@ const buildTableNodeProcessor = (withMdx) => {
80887
80925
  const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tableCell);
80888
80926
  // `mdxjs` goes first (= lowest priority): its `mdxJsx` also claims `text` + `<`,
80889
80927
  // and `legacyVariable` has to keep winning that race so `<<var>>` still parses.
80928
+ // `mdxishExpressionFromMarkdown` follows `mdxFromMarkdown` to override its expression exit.
80890
80929
  return unified()
80891
80930
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), ...micromarkExtensions])
80892
- .data('fromMarkdownExtensions', [...(withMdx ? [mdxFromMarkdown()] : []), ...fromMarkdownExtensions])
80931
+ .data('fromMarkdownExtensions', [
80932
+ ...(withMdx ? [mdxFromMarkdown(), mdxishExpressionFromMarkdown()] : []),
80933
+ ...fromMarkdownExtensions,
80934
+ ])
80893
80935
  .use(remarkParse)
80894
80936
  .use(normalize_malformed_md_syntax)
80895
80937
  .use([[callouts, { isMdxish: true }], code_tabs])
@@ -107790,22 +107832,28 @@ const BARE_VARIABLE_REGEX = new RegExp(`^${variable_.MDX_VARIABLE_REGEXP}$`, 'u'
107790
107832
  // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
107791
107833
  // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
107792
107834
  const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
107835
+ const ALT_VARIABLE_REGEX = new RegExp(`${MDX_VARIABLE_REGEX.source}|${variable_.VARIABLE_REGEXP}`, 'gu');
107793
107836
  /**
107794
107837
  * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
107795
107838
  * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
107796
107839
  *
107797
107840
  * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
107798
- * an MDX surface, and `{user.*}` is the syntax authors use there.
107841
+ * an MDX surface, and `{user.*}` is the syntax authors use there. Image alt is the exception, resolved
107842
+ * in the same pass so a substituted value is never rescanned (RM-10865).
107799
107843
  */
107800
- function resolveAttributeVariables(value, user) {
107801
- if (!value.includes('{user'))
107844
+ function resolveAttributeVariables(value, user, isImageAlt = false) {
107845
+ if (!isImageAlt && !value.includes('{user'))
107802
107846
  return value;
107803
107847
  return value
107804
107848
  .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
107805
- .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
107806
- if (escapePrefix || escapeSuffix)
107807
- return source;
107808
- return stringifyVariableValue(user[name]);
107849
+ .replace(isImageAlt ? ALT_VARIABLE_REGEX : MDX_VARIABLE_REGEX, (source, _prefix, name, _suffix, legacyName) => {
107850
+ // Variable names can't contain a backslash, so one marks an escaped reference.
107851
+ if (source.includes('\\'))
107852
+ return isImageAlt ? source.replaceAll('\\', '') : source;
107853
+ const key = (name ?? legacyName).trim();
107854
+ if (key.startsWith('glossary:'))
107855
+ return key.slice('glossary:'.length).trim();
107856
+ return stringifyVariableValue(user[key]);
107809
107857
  });
107810
107858
  }
107811
107859
  /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
@@ -108758,8 +108806,10 @@ function exportComponentsForRehype(components) {
108758
108806
  * deliberately leaves mermaid alone; resolving it again here would override that.
108759
108807
  */
108760
108808
  const CODE_TAG_NAMES = new Set(['code']);
108809
+ const isImageAlt = (tagName, key) => tagName === 'img' && key === 'alt';
108761
108810
  /**
108762
- * Resolve `{user.*}` in string-valued props. Body text is handled by the `Variable` component, but
108811
+ * Resolve `{user.*}` in string-valued props, plus legacy `<<...>>` in img alt, where markdown flattens
108812
+ * body variables to text (RM-10865). Body text is handled by the `Variable` component, but
108763
108813
  * attributes are plain strings, so they are substituted here — at render time, so that a
108764
108814
  * server-parsed (user-agnostic) tree resolves against the current reader's variables.
108765
108815
  */
@@ -108769,7 +108819,7 @@ function resolveVariablesInProps(props, user, tagName) {
108769
108819
  const isDocumentContent = RAW_HTML_PROP_NAMES.has(key) || (isCodeTag && key === 'value');
108770
108820
  if (typeof value !== 'string' || isDocumentContent)
108771
108821
  return [key, value];
108772
- return [key, resolveAttributeVariables(value, user)];
108822
+ return [key, resolveAttributeVariables(value, user, isImageAlt(tagName, key))];
108773
108823
  });
108774
108824
  return Object.fromEntries(resolvedEntries);
108775
108825
  }
@@ -109459,7 +109509,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
109459
109509
  /******/ // startup
109460
109510
  /******/ // Load entry module and return exports
109461
109511
  /******/ // This entry module used 'module' so it can't be inlined
109462
- /******/ let __webpack_exports__ = __webpack_require__(8846);
109512
+ /******/ let __webpack_exports__ = __webpack_require__(714);
109463
109513
  /******/
109464
109514
  /******/ return __webpack_exports__;
109465
109515
  /******/ })()
package/dist/main.node.js CHANGED
@@ -19111,7 +19111,7 @@ module.exports = function () {
19111
19111
 
19112
19112
  /***/ },
19113
19113
 
19114
- /***/ 6831
19114
+ /***/ 3975
19115
19115
  (module, __webpack_exports__, __webpack_require__) {
19116
19116
 
19117
19117
  "use strict";
@@ -93320,6 +93320,26 @@ const mdxToHast = () => tree => {
93320
93320
  };
93321
93321
  /* harmony default export */ const mdx_to_hast = (mdxToHast);
93322
93322
 
93323
+ ;// ./lib/mdast-util/mdx-expression/index.ts
93324
+
93325
+ const upstream = mdxExpressionFromMarkdown();
93326
+ /**
93327
+ * An image's alt is its label flattened to text, which drops an expression's braces. Keep the
93328
+ * `{...}` source there so `{user.*}` still resolves in alt at render time (RM-10865).
93329
+ */
93330
+ function exitTextExpression(token) {
93331
+ upstream.exit.mdxTextExpression.call(this, token);
93332
+ if (!this.stack.some(node => node.type === 'image'))
93333
+ return;
93334
+ const { children } = this.stack[this.stack.length - 1];
93335
+ const { position } = children[children.length - 1];
93336
+ children[children.length - 1] = { type: 'text', value: this.sliceSerialize(token), position };
93337
+ }
93338
+ /** Upstream `mdxExpressionFromMarkdown`, overriding only the text-expression exit to keep `{...}` source in image alt. */
93339
+ function mdxishExpressionFromMarkdown() {
93340
+ return { ...upstream, exit: { ...upstream.exit, mdxTextExpression: exitTextExpression } };
93341
+ }
93342
+
93323
93343
  ;// ./lib/mdast-util/empty-task-list-item/index.ts
93324
93344
  /**
93325
93345
  * Normalizes list items that are written as only `[ ]` or `[x]` into GFM task
@@ -93552,6 +93572,16 @@ function exitlegacyVariableValue(token) {
93552
93572
  function exitlegacyVariable(token) {
93553
93573
  const ctx = legacy_variable_contextMap.get(token);
93554
93574
  const serialized = this.sliceSerialize(token);
93575
+ // An escaped `\<<name>>` (or `\<<name\>>`) is literal text, as in the legacy parser.
93576
+ if (serialized.startsWith('\\')) {
93577
+ const literal = serialized.slice(1).replace('\\>>', '>>');
93578
+ // An image's alt is flattened to a string, so keep the escape for render-time resolution to honor.
93579
+ const inImageAlt = this.stack.some(node => node.type === 'image');
93580
+ this.enter({ type: 'text', value: inImageAlt ? `\\${literal}` : literal }, token);
93581
+ this.exit(token);
93582
+ legacy_variable_contextMap.delete(token);
93583
+ return;
93584
+ }
93555
93585
  const variableName = serialized.startsWith('<<') && serialized.endsWith('>>')
93556
93586
  ? serialized.slice(2, -2)
93557
93587
  : ctx?.value ?? '';
@@ -95145,9 +95175,16 @@ const legacyVariableConstruct = {
95145
95175
  function syntax_tokenize(effects, ok, nok) {
95146
95176
  let hasValue = false;
95147
95177
  const start = (code) => {
95178
+ effects.enter('legacyVariable');
95179
+ // Claim `\<<name>>` before CommonMark's character escape drops the backslash, as the legacy parser did.
95180
+ if (code !== codes.backslash)
95181
+ return open(code);
95182
+ effects.consume(code); // \
95183
+ return open;
95184
+ };
95185
+ const open = (code) => {
95148
95186
  if (code !== codes.lessThan)
95149
95187
  return nok(code);
95150
- effects.enter('legacyVariable');
95151
95188
  effects.enter('legacyVariableMarkerStart');
95152
95189
  effects.consume(code); // <
95153
95190
  return open2;
@@ -95187,7 +95224,7 @@ function syntax_tokenize(effects, ok, nok) {
95187
95224
  }
95188
95225
  function legacyVariable() {
95189
95226
  return {
95190
- text: { [codes.lessThan]: legacyVariableConstruct },
95227
+ text: { [codes.backslash]: legacyVariableConstruct, [codes.lessThan]: legacyVariableConstruct },
95191
95228
  };
95192
95229
  }
95193
95230
 
@@ -97511,12 +97548,12 @@ const REGISTRY = {
97511
97548
  magicBlock: { syntax: magicBlock, fromMarkdown: magicBlockFromMarkdown },
97512
97549
  // Two `{` tokenizers, never registered together: the document parser takes text
97513
97550
  // only, the component-body re-parser needs flow too for multi-line expressions.
97514
- mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxExpressionFromMarkdown, expression: true },
97551
+ mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxishExpressionFromMarkdown, expression: true },
97515
97552
  // `allowEmpty` is the package default; passed explicitly because `mdx-jsx`
97516
97553
  // registers the same tokenizer with it off for attribute expressions.
97517
97554
  mdxExpression: {
97518
97555
  syntax: () => mdxExpression({ allowEmpty: true }),
97519
- fromMarkdown: mdxExpressionFromMarkdown,
97556
+ fromMarkdown: mdxishExpressionFromMarkdown,
97520
97557
  expression: true,
97521
97558
  },
97522
97559
  mdxComponent: { syntax: mdxComponent, fromMarkdown: mdxComponentFromMarkdown },
@@ -101116,6 +101153,7 @@ const splitHtmlWithNestedTables = (node, source) => {
101116
101153
 
101117
101154
 
101118
101155
 
101156
+
101119
101157
 
101120
101158
 
101121
101159
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
@@ -101136,9 +101174,13 @@ const buildTableNodeProcessor = (withMdx) => {
101136
101174
  const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tableCell);
101137
101175
  // `mdxjs` goes first (= lowest priority): its `mdxJsx` also claims `text` + `<`,
101138
101176
  // and `legacyVariable` has to keep winning that race so `<<var>>` still parses.
101177
+ // `mdxishExpressionFromMarkdown` follows `mdxFromMarkdown` to override its expression exit.
101139
101178
  return unified()
101140
101179
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), ...micromarkExtensions])
101141
- .data('fromMarkdownExtensions', [...(withMdx ? [mdxFromMarkdown()] : []), ...fromMarkdownExtensions])
101180
+ .data('fromMarkdownExtensions', [
101181
+ ...(withMdx ? [mdxFromMarkdown(), mdxishExpressionFromMarkdown()] : []),
101182
+ ...fromMarkdownExtensions,
101183
+ ])
101142
101184
  .use(remarkParse)
101143
101185
  .use(normalize_malformed_md_syntax)
101144
101186
  .use([[callouts, { isMdxish: true }], code_tabs])
@@ -128039,22 +128081,28 @@ const BARE_VARIABLE_REGEX = new RegExp(`^${variable_dist.MDX_VARIABLE_REGEXP}$`,
128039
128081
  // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
128040
128082
  // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
128041
128083
  const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
128084
+ const ALT_VARIABLE_REGEX = new RegExp(`${MDX_VARIABLE_REGEX.source}|${variable_dist.VARIABLE_REGEXP}`, 'gu');
128042
128085
  /**
128043
128086
  * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
128044
128087
  * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
128045
128088
  *
128046
128089
  * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
128047
- * an MDX surface, and `{user.*}` is the syntax authors use there.
128090
+ * an MDX surface, and `{user.*}` is the syntax authors use there. Image alt is the exception, resolved
128091
+ * in the same pass so a substituted value is never rescanned (RM-10865).
128048
128092
  */
128049
- function resolveAttributeVariables(value, user) {
128050
- if (!value.includes('{user'))
128093
+ function resolveAttributeVariables(value, user, isImageAlt = false) {
128094
+ if (!isImageAlt && !value.includes('{user'))
128051
128095
  return value;
128052
128096
  return value
128053
128097
  .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
128054
- .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
128055
- if (escapePrefix || escapeSuffix)
128056
- return source;
128057
- return stringifyVariableValue(user[name]);
128098
+ .replace(isImageAlt ? ALT_VARIABLE_REGEX : MDX_VARIABLE_REGEX, (source, _prefix, name, _suffix, legacyName) => {
128099
+ // Variable names can't contain a backslash, so one marks an escaped reference.
128100
+ if (source.includes('\\'))
128101
+ return isImageAlt ? source.replaceAll('\\', '') : source;
128102
+ const key = (name ?? legacyName).trim();
128103
+ if (key.startsWith('glossary:'))
128104
+ return key.slice('glossary:'.length).trim();
128105
+ return stringifyVariableValue(user[key]);
128058
128106
  });
128059
128107
  }
128060
128108
  /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
@@ -129007,8 +129055,10 @@ function exportComponentsForRehype(components) {
129007
129055
  * deliberately leaves mermaid alone; resolving it again here would override that.
129008
129056
  */
129009
129057
  const CODE_TAG_NAMES = new Set(['code']);
129058
+ const isImageAlt = (tagName, key) => tagName === 'img' && key === 'alt';
129010
129059
  /**
129011
- * Resolve `{user.*}` in string-valued props. Body text is handled by the `Variable` component, but
129060
+ * Resolve `{user.*}` in string-valued props, plus legacy `<<...>>` in img alt, where markdown flattens
129061
+ * body variables to text (RM-10865). Body text is handled by the `Variable` component, but
129012
129062
  * attributes are plain strings, so they are substituted here — at render time, so that a
129013
129063
  * server-parsed (user-agnostic) tree resolves against the current reader's variables.
129014
129064
  */
@@ -129018,7 +129068,7 @@ function resolveVariablesInProps(props, user, tagName) {
129018
129068
  const isDocumentContent = RAW_HTML_PROP_NAMES.has(key) || (isCodeTag && key === 'value');
129019
129069
  if (typeof value !== 'string' || isDocumentContent)
129020
129070
  return [key, value];
129021
- return [key, resolveAttributeVariables(value, user)];
129071
+ return [key, resolveAttributeVariables(value, user, isImageAlt(tagName, key))];
129022
129072
  });
129023
129073
  return Object.fromEntries(resolvedEntries);
129024
129074
  }
@@ -129767,7 +129817,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129767
129817
  /******/ // startup
129768
129818
  /******/ // Load entry module and return exports
129769
129819
  /******/ // This entry module used 'module' so it can't be inlined
129770
- /******/ let __webpack_exports__ = __webpack_require__(6831);
129820
+ /******/ let __webpack_exports__ = __webpack_require__(3975);
129771
129821
  /******/ module.exports = __webpack_exports__;
129772
129822
  /******/
129773
129823
  /******/ })()