@readme/markdown 15.9.2 → 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.
@@ -56,3 +56,5 @@ export declare const TOKENIZER_MDX_COMPONENT_EXCLUDED_TAGS: Set<string>;
56
56
  * run after rehype (where hast `tagName` is normalized to lowercase).
57
57
  */
58
58
  export declare const INLINE_COMPONENT_TAGS_LOWER: Set<string>;
59
+ /** Raw markup props injected with `dangerouslySetInnerHTML` by HTMLBlock and Embed; never interpolate reader values. */
60
+ export declare const RAW_HTML_PROP_NAMES: Set<string>;
@@ -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,6 +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;
10
+ /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
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";
@@ -13191,6 +13191,8 @@ const TOKENIZER_MDX_COMPONENT_EXCLUDED_TAGS = new Set([
13191
13191
  * run after rehype (where hast `tagName` is normalized to lowercase).
13192
13192
  */
13193
13193
  const INLINE_COMPONENT_TAGS_LOWER = new Set([...INLINE_COMPONENT_TAGS].map(t => t.toLowerCase()));
13194
+ /** Raw markup props injected with `dangerouslySetInnerHTML` by HTMLBlock and Embed; never interpolate reader values. */
13195
+ const RAW_HTML_PROP_NAMES = new Set(['html']);
13194
13196
 
13195
13197
  ;// ./node_modules/github-slugger/regex.js
13196
13198
  // This module is generated by `script/`.
@@ -73069,6 +73071,26 @@ const mdxToHast = () => tree => {
73069
73071
  };
73070
73072
  /* harmony default export */ const mdx_to_hast = (mdxToHast);
73071
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
+
73072
73094
  ;// ./lib/mdast-util/empty-task-list-item/index.ts
73073
73095
  /**
73074
73096
  * Normalizes list items that are written as only `[ ]` or `[x]` into GFM task
@@ -73301,6 +73323,16 @@ function exitlegacyVariableValue(token) {
73301
73323
  function exitlegacyVariable(token) {
73302
73324
  const ctx = legacy_variable_contextMap.get(token);
73303
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
+ }
73304
73336
  const variableName = serialized.startsWith('<<') && serialized.endsWith('>>')
73305
73337
  ? serialized.slice(2, -2)
73306
73338
  : ctx?.value ?? '';
@@ -74894,9 +74926,16 @@ const legacyVariableConstruct = {
74894
74926
  function syntax_tokenize(effects, ok, nok) {
74895
74927
  let hasValue = false;
74896
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) => {
74897
74937
  if (code !== codes.lessThan)
74898
74938
  return nok(code);
74899
- effects.enter('legacyVariable');
74900
74939
  effects.enter('legacyVariableMarkerStart');
74901
74940
  effects.consume(code); // <
74902
74941
  return open2;
@@ -74936,7 +74975,7 @@ function syntax_tokenize(effects, ok, nok) {
74936
74975
  }
74937
74976
  function legacyVariable() {
74938
74977
  return {
74939
- text: { [codes.lessThan]: legacyVariableConstruct },
74978
+ text: { [codes.backslash]: legacyVariableConstruct, [codes.lessThan]: legacyVariableConstruct },
74940
74979
  };
74941
74980
  }
74942
74981
 
@@ -77260,12 +77299,12 @@ const REGISTRY = {
77260
77299
  magicBlock: { syntax: magicBlock, fromMarkdown: magicBlockFromMarkdown },
77261
77300
  // Two `{` tokenizers, never registered together: the document parser takes text
77262
77301
  // only, the component-body re-parser needs flow too for multi-line expressions.
77263
- mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxExpressionFromMarkdown, expression: true },
77302
+ mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxishExpressionFromMarkdown, expression: true },
77264
77303
  // `allowEmpty` is the package default; passed explicitly because `mdx-jsx`
77265
77304
  // registers the same tokenizer with it off for attribute expressions.
77266
77305
  mdxExpression: {
77267
77306
  syntax: () => mdxExpression({ allowEmpty: true }),
77268
- fromMarkdown: mdxExpressionFromMarkdown,
77307
+ fromMarkdown: mdxishExpressionFromMarkdown,
77269
77308
  expression: true,
77270
77309
  },
77271
77310
  mdxComponent: { syntax: mdxComponent, fromMarkdown: mdxComponentFromMarkdown },
@@ -80865,6 +80904,7 @@ const splitHtmlWithNestedTables = (node, source) => {
80865
80904
 
80866
80905
 
80867
80906
 
80907
+
80868
80908
 
80869
80909
 
80870
80910
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
@@ -80885,9 +80925,13 @@ const buildTableNodeProcessor = (withMdx) => {
80885
80925
  const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tableCell);
80886
80926
  // `mdxjs` goes first (= lowest priority): its `mdxJsx` also claims `text` + `<`,
80887
80927
  // and `legacyVariable` has to keep winning that race so `<<var>>` still parses.
80928
+ // `mdxishExpressionFromMarkdown` follows `mdxFromMarkdown` to override its expression exit.
80888
80929
  return unified()
80889
80930
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), ...micromarkExtensions])
80890
- .data('fromMarkdownExtensions', [...(withMdx ? [mdxFromMarkdown()] : []), ...fromMarkdownExtensions])
80931
+ .data('fromMarkdownExtensions', [
80932
+ ...(withMdx ? [mdxFromMarkdown(), mdxishExpressionFromMarkdown()] : []),
80933
+ ...fromMarkdownExtensions,
80934
+ ])
80891
80935
  .use(remarkParse)
80892
80936
  .use(normalize_malformed_md_syntax)
80893
80937
  .use([[callouts, { isMdxish: true }], code_tabs])
@@ -107775,33 +107819,81 @@ function repairMistakenTableClosers(content) {
107775
107819
  return applyInserts(content, edits).value;
107776
107820
  }
107777
107821
 
107822
+ ;// ./lib/utils/mdxish/mdxish-variables.ts
107823
+
107824
+
107825
+ // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
107826
+ // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
107827
+ // which either evaluated already or is meant to stay literal.
107828
+ const MDX_VARIABLE_REGEX = new RegExp(`(?<!\\$)${variable_.MDX_VARIABLE_REGEXP}`, 'gu');
107829
+ // A whole attribute expression that is one `user.*` reference, e.g. the `user.name` in `title={user.name}`.
107830
+ const BARE_VARIABLE_REGEX = new RegExp(`^${variable_.MDX_VARIABLE_REGEXP}$`, 'u');
107831
+ // Bracket notation names the same variable as dot notation, so normalize it before substituting.
107832
+ // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
107833
+ // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
107834
+ const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
107835
+ const ALT_VARIABLE_REGEX = new RegExp(`${MDX_VARIABLE_REGEX.source}|${variable_.VARIABLE_REGEXP}`, 'gu');
107836
+ /**
107837
+ * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
107838
+ * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
107839
+ *
107840
+ * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
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).
107843
+ */
107844
+ function resolveAttributeVariables(value, user, isImageAlt = false) {
107845
+ if (!isImageAlt && !value.includes('{user'))
107846
+ return value;
107847
+ return value
107848
+ .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
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]);
107857
+ });
107858
+ }
107859
+ /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
107860
+ const isBareVariableReference = (source) => BARE_VARIABLE_REGEX.test(`{${source.trim()}}`.replace(BRACKET_NOTATION_REGEX, '{user.$1}'));
107861
+
107778
107862
  ;// ./processor/transform/mdxish/resolve-deferred-attribute-expression-props.ts
107779
107863
 
107780
107864
 
107781
107865
 
107782
107866
 
107867
+
107868
+
107869
+
107783
107870
  /**
107784
107871
  * Resolve attribute expressions that `mdxJsxElementHandler` deferred.
107785
107872
  *
107786
107873
  * The handler stashed each attribute expression's source in `node.data.deferredExpressions`
107787
107874
  * (keyed by prop name) instead of evaluating eagerly, because an eager result can be a React
107788
107875
  * element or function that rehypeRaw's `structuredClone` passthrough rejects. Now that we're
107789
- * past the clone, evaluate each source with a scope of `{ React, ...mdxishScope }` and write
107876
+ * past the clone, evaluate each source with a scope of `{ user, React, ...mdxishScope }` and write
107790
107877
  * the real value into `properties` (e.g. an array of objects whose fields are React elements).
107791
107878
  *
107792
107879
  * Must run after `rehypeRaw` (past the clone) but before `normalizeMdxJsxNodes` rewrites these
107793
107880
  * `mdx-jsx` nodes into plain elements. Skipped in safeMode, which keeps expressions literal.
107794
107881
  */
107795
- const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
107882
+ const resolveDeferredAttributeExpressionProps = ({ variables } = {}) => (tree, file) => {
107796
107883
  const scope = { ...file.data.mdxishScope, React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()) };
107884
+ // `user` only when variables were supplied, as in `evaluateExpressions`.
107885
+ const userScope = variables ? { user: user(variables), ...scope } : scope;
107797
107886
  visit(tree, 'mdx-jsx', (node) => {
107798
107887
  const deferredExpressions = node.data?.deferredExpressions;
107799
107888
  if (!deferredExpressions)
107800
107889
  return;
107801
107890
  const properties = node.properties;
107802
107891
  Object.entries(deferredExpressions).forEach(([name, source]) => {
107892
+ // Raw markup never gets `user`: a reader's values reaching `dangerouslySetInnerHTML` is an injection
107893
+ // vector. A bare `{user.x}` keeps render-time substitution, which stringifies structured values.
107894
+ const canReadUser = !RAW_HTML_PROP_NAMES.has(name) && !isBareVariableReference(source);
107803
107895
  try {
107804
- const result = evalExpression(source, scope);
107896
+ const result = evalExpression(source, canReadUser ? userScope : scope);
107805
107897
  // hast/HTML `style` is a plain CSS string; a `style={{...}}` expression evaluates to
107806
107898
  // a JS object, which must be serialized or it renders as the literal "[object Object]".
107807
107899
  properties[name] = name === 'style' && style_object_to_css_isPlainObject(result) ? styleObjectToCssText(result) : result;
@@ -108465,7 +108557,7 @@ function mdxish(mdContent, opts = {}) {
108465
108557
  .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
108466
108558
  .use(rehypeRaw, { passThrough: ['html-block', 'mdx-jsx'] }) // MDX JSX nodes bypass parse5's string-only HTML round-trip
108467
108559
  .use(restoreBooleanProperties)
108468
- .use(safeMode ? undefined : resolve_deferred_attribute_expression_props) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
108560
+ .use(safeMode ? undefined : resolve_deferred_attribute_expression_props, { variables }) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
108469
108561
  .use(normalize_mdx_jsx_nodes) // Rewrite `mdx-jsx` back to standard `element` nodes for downstream plugins
108470
108562
  .use(sanitize ? rehypeStripTags : undefined) // Strip STRIPPED_TAG_NAMES elements; this pipeline has no sanitization step
108471
108563
  .use(rehypeFlattenTableCellParagraphs) // Remove <p> wrappers inside table cells to prevent margin issues
@@ -108671,36 +108763,6 @@ const makeUseMDXComponents = (more = {}) => {
108671
108763
  };
108672
108764
  /* harmony default export */ const makeUseMdxComponents = (makeUseMDXComponents);
108673
108765
 
108674
- ;// ./lib/utils/mdxish/mdxish-variables.ts
108675
-
108676
-
108677
- // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
108678
- // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
108679
- // which either evaluated already or is meant to stay literal.
108680
- const MDX_VARIABLE_REGEX = new RegExp(`(?<!\\$)${variable_.MDX_VARIABLE_REGEXP}`, 'gu');
108681
- // Bracket notation names the same variable as dot notation, so normalize it before substituting.
108682
- // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
108683
- // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
108684
- const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
108685
- /**
108686
- * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
108687
- * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
108688
- *
108689
- * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
108690
- * an MDX surface, and `{user.*}` is the syntax authors use there.
108691
- */
108692
- function resolveAttributeVariables(value, user) {
108693
- if (!value.includes('{user'))
108694
- return value;
108695
- return value
108696
- .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
108697
- .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
108698
- if (escapePrefix || escapeSuffix)
108699
- return source;
108700
- return stringifyVariableValue(user[name]);
108701
- });
108702
- }
108703
-
108704
108766
  ;// ./lib/utils/mdxish/mdxish-render-utils.tsx
108705
108767
 
108706
108768
 
@@ -108711,6 +108773,7 @@ function resolveAttributeVariables(value, user) {
108711
108773
 
108712
108774
 
108713
108775
 
108776
+
108714
108777
  /** Flatten CustomComponents into a component map for rehype-react */
108715
108778
  function exportComponentsForRehype(components) {
108716
108779
  const exported = Object.entries(components).reduce((memo, [tag, mod]) => {
@@ -108743,10 +108806,10 @@ function exportComponentsForRehype(components) {
108743
108806
  * deliberately leaves mermaid alone; resolving it again here would override that.
108744
108807
  */
108745
108808
  const CODE_TAG_NAMES = new Set(['code']);
108746
- /** Raw markup injected with `dangerouslySetInnerHTML` by HTMLBlock and Embed — never interpolate. */
108747
- const RAW_HTML_PROP_NAMES = new Set(['html']);
108809
+ const isImageAlt = (tagName, key) => tagName === 'img' && key === 'alt';
108748
108810
  /**
108749
- * 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
108750
108813
  * attributes are plain strings, so they are substituted here — at render time, so that a
108751
108814
  * server-parsed (user-agnostic) tree resolves against the current reader's variables.
108752
108815
  */
@@ -108756,7 +108819,7 @@ function resolveVariablesInProps(props, user, tagName) {
108756
108819
  const isDocumentContent = RAW_HTML_PROP_NAMES.has(key) || (isCodeTag && key === 'value');
108757
108820
  if (typeof value !== 'string' || isDocumentContent)
108758
108821
  return [key, value];
108759
- return [key, resolveAttributeVariables(value, user)];
108822
+ return [key, resolveAttributeVariables(value, user, isImageAlt(tagName, key))];
108760
108823
  });
108761
108824
  return Object.fromEntries(resolvedEntries);
108762
108825
  }
@@ -109446,7 +109509,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
109446
109509
  /******/ // startup
109447
109510
  /******/ // Load entry module and return exports
109448
109511
  /******/ // This entry module used 'module' so it can't be inlined
109449
- /******/ let __webpack_exports__ = __webpack_require__(8846);
109512
+ /******/ let __webpack_exports__ = __webpack_require__(714);
109450
109513
  /******/
109451
109514
  /******/ return __webpack_exports__;
109452
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";
@@ -25789,6 +25789,8 @@ const TOKENIZER_MDX_COMPONENT_EXCLUDED_TAGS = new Set([
25789
25789
  * run after rehype (where hast `tagName` is normalized to lowercase).
25790
25790
  */
25791
25791
  const INLINE_COMPONENT_TAGS_LOWER = new Set([...INLINE_COMPONENT_TAGS].map(t => t.toLowerCase()));
25792
+ /** Raw markup props injected with `dangerouslySetInnerHTML` by HTMLBlock and Embed; never interpolate reader values. */
25793
+ const RAW_HTML_PROP_NAMES = new Set(['html']);
25792
25794
 
25793
25795
  ;// ./node_modules/github-slugger/regex.js
25794
25796
  // This module is generated by `script/`.
@@ -93318,6 +93320,26 @@ const mdxToHast = () => tree => {
93318
93320
  };
93319
93321
  /* harmony default export */ const mdx_to_hast = (mdxToHast);
93320
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
+
93321
93343
  ;// ./lib/mdast-util/empty-task-list-item/index.ts
93322
93344
  /**
93323
93345
  * Normalizes list items that are written as only `[ ]` or `[x]` into GFM task
@@ -93550,6 +93572,16 @@ function exitlegacyVariableValue(token) {
93550
93572
  function exitlegacyVariable(token) {
93551
93573
  const ctx = legacy_variable_contextMap.get(token);
93552
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
+ }
93553
93585
  const variableName = serialized.startsWith('<<') && serialized.endsWith('>>')
93554
93586
  ? serialized.slice(2, -2)
93555
93587
  : ctx?.value ?? '';
@@ -95143,9 +95175,16 @@ const legacyVariableConstruct = {
95143
95175
  function syntax_tokenize(effects, ok, nok) {
95144
95176
  let hasValue = false;
95145
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) => {
95146
95186
  if (code !== codes.lessThan)
95147
95187
  return nok(code);
95148
- effects.enter('legacyVariable');
95149
95188
  effects.enter('legacyVariableMarkerStart');
95150
95189
  effects.consume(code); // <
95151
95190
  return open2;
@@ -95185,7 +95224,7 @@ function syntax_tokenize(effects, ok, nok) {
95185
95224
  }
95186
95225
  function legacyVariable() {
95187
95226
  return {
95188
- text: { [codes.lessThan]: legacyVariableConstruct },
95227
+ text: { [codes.backslash]: legacyVariableConstruct, [codes.lessThan]: legacyVariableConstruct },
95189
95228
  };
95190
95229
  }
95191
95230
 
@@ -97509,12 +97548,12 @@ const REGISTRY = {
97509
97548
  magicBlock: { syntax: magicBlock, fromMarkdown: magicBlockFromMarkdown },
97510
97549
  // Two `{` tokenizers, never registered together: the document parser takes text
97511
97550
  // only, the component-body re-parser needs flow too for multi-line expressions.
97512
- mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxExpressionFromMarkdown, expression: true },
97551
+ mdxExpressionLenient: { syntax: mdxExpressionLenient, fromMarkdown: mdxishExpressionFromMarkdown, expression: true },
97513
97552
  // `allowEmpty` is the package default; passed explicitly because `mdx-jsx`
97514
97553
  // registers the same tokenizer with it off for attribute expressions.
97515
97554
  mdxExpression: {
97516
97555
  syntax: () => mdxExpression({ allowEmpty: true }),
97517
- fromMarkdown: mdxExpressionFromMarkdown,
97556
+ fromMarkdown: mdxishExpressionFromMarkdown,
97518
97557
  expression: true,
97519
97558
  },
97520
97559
  mdxComponent: { syntax: mdxComponent, fromMarkdown: mdxComponentFromMarkdown },
@@ -101114,6 +101153,7 @@ const splitHtmlWithNestedTables = (node, source) => {
101114
101153
 
101115
101154
 
101116
101155
 
101156
+
101117
101157
 
101118
101158
 
101119
101159
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
@@ -101134,9 +101174,13 @@ const buildTableNodeProcessor = (withMdx) => {
101134
101174
  const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tableCell);
101135
101175
  // `mdxjs` goes first (= lowest priority): its `mdxJsx` also claims `text` + `<`,
101136
101176
  // and `legacyVariable` has to keep winning that race so `<<var>>` still parses.
101177
+ // `mdxishExpressionFromMarkdown` follows `mdxFromMarkdown` to override its expression exit.
101137
101178
  return unified()
101138
101179
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), ...micromarkExtensions])
101139
- .data('fromMarkdownExtensions', [...(withMdx ? [mdxFromMarkdown()] : []), ...fromMarkdownExtensions])
101180
+ .data('fromMarkdownExtensions', [
101181
+ ...(withMdx ? [mdxFromMarkdown(), mdxishExpressionFromMarkdown()] : []),
101182
+ ...fromMarkdownExtensions,
101183
+ ])
101140
101184
  .use(remarkParse)
101141
101185
  .use(normalize_malformed_md_syntax)
101142
101186
  .use([[callouts, { isMdxish: true }], code_tabs])
@@ -128024,33 +128068,81 @@ function repairMistakenTableClosers(content) {
128024
128068
  return applyInserts(content, edits).value;
128025
128069
  }
128026
128070
 
128071
+ ;// ./lib/utils/mdxish/mdxish-variables.ts
128072
+
128073
+
128074
+ // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
128075
+ // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
128076
+ // which either evaluated already or is meant to stay literal.
128077
+ const MDX_VARIABLE_REGEX = new RegExp(`(?<!\\$)${variable_dist.MDX_VARIABLE_REGEXP}`, 'gu');
128078
+ // A whole attribute expression that is one `user.*` reference, e.g. the `user.name` in `title={user.name}`.
128079
+ const BARE_VARIABLE_REGEX = new RegExp(`^${variable_dist.MDX_VARIABLE_REGEXP}$`, 'u');
128080
+ // Bracket notation names the same variable as dot notation, so normalize it before substituting.
128081
+ // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
128082
+ // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
128083
+ const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
128084
+ const ALT_VARIABLE_REGEX = new RegExp(`${MDX_VARIABLE_REGEX.source}|${variable_dist.VARIABLE_REGEXP}`, 'gu');
128085
+ /**
128086
+ * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
128087
+ * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
128088
+ *
128089
+ * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
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).
128092
+ */
128093
+ function resolveAttributeVariables(value, user, isImageAlt = false) {
128094
+ if (!isImageAlt && !value.includes('{user'))
128095
+ return value;
128096
+ return value
128097
+ .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
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]);
128106
+ });
128107
+ }
128108
+ /** Whether an attribute expression's source is a bare `user.*` reference, left for render-time substitution. */
128109
+ const isBareVariableReference = (source) => BARE_VARIABLE_REGEX.test(`{${source.trim()}}`.replace(BRACKET_NOTATION_REGEX, '{user.$1}'));
128110
+
128027
128111
  ;// ./processor/transform/mdxish/resolve-deferred-attribute-expression-props.ts
128028
128112
 
128029
128113
 
128030
128114
 
128031
128115
 
128116
+
128117
+
128118
+
128032
128119
  /**
128033
128120
  * Resolve attribute expressions that `mdxJsxElementHandler` deferred.
128034
128121
  *
128035
128122
  * The handler stashed each attribute expression's source in `node.data.deferredExpressions`
128036
128123
  * (keyed by prop name) instead of evaluating eagerly, because an eager result can be a React
128037
128124
  * element or function that rehypeRaw's `structuredClone` passthrough rejects. Now that we're
128038
- * past the clone, evaluate each source with a scope of `{ React, ...mdxishScope }` and write
128125
+ * past the clone, evaluate each source with a scope of `{ user, React, ...mdxishScope }` and write
128039
128126
  * the real value into `properties` (e.g. an array of objects whose fields are React elements).
128040
128127
  *
128041
128128
  * Must run after `rehypeRaw` (past the clone) but before `normalizeMdxJsxNodes` rewrites these
128042
128129
  * `mdx-jsx` nodes into plain elements. Skipped in safeMode, which keeps expressions literal.
128043
128130
  */
128044
- const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
128131
+ const resolveDeferredAttributeExpressionProps = ({ variables } = {}) => (tree, file) => {
128045
128132
  const scope = { ...file.data.mdxishScope, React: (external_react_default()) };
128133
+ // `user` only when variables were supplied, as in `evaluateExpressions`.
128134
+ const userScope = variables ? { user: user(variables), ...scope } : scope;
128046
128135
  visit(tree, 'mdx-jsx', (node) => {
128047
128136
  const deferredExpressions = node.data?.deferredExpressions;
128048
128137
  if (!deferredExpressions)
128049
128138
  return;
128050
128139
  const properties = node.properties;
128051
128140
  Object.entries(deferredExpressions).forEach(([name, source]) => {
128141
+ // Raw markup never gets `user`: a reader's values reaching `dangerouslySetInnerHTML` is an injection
128142
+ // vector. A bare `{user.x}` keeps render-time substitution, which stringifies structured values.
128143
+ const canReadUser = !RAW_HTML_PROP_NAMES.has(name) && !isBareVariableReference(source);
128052
128144
  try {
128053
- const result = evalExpression(source, scope);
128145
+ const result = evalExpression(source, canReadUser ? userScope : scope);
128054
128146
  // hast/HTML `style` is a plain CSS string; a `style={{...}}` expression evaluates to
128055
128147
  // a JS object, which must be serialized or it renders as the literal "[object Object]".
128056
128148
  properties[name] = name === 'style' && style_object_to_css_isPlainObject(result) ? styleObjectToCssText(result) : result;
@@ -128714,7 +128806,7 @@ function mdxish(mdContent, opts = {}) {
128714
128806
  .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
128715
128807
  .use(rehypeRaw, { passThrough: ['html-block', 'mdx-jsx'] }) // MDX JSX nodes bypass parse5's string-only HTML round-trip
128716
128808
  .use(restoreBooleanProperties)
128717
- .use(safeMode ? undefined : resolve_deferred_attribute_expression_props) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
128809
+ .use(safeMode ? undefined : resolve_deferred_attribute_expression_props, { variables }) // Evaluate deferred attribute expressions on mdx-jsx nodes (now past rehypeRaw's clone)
128718
128810
  .use(normalize_mdx_jsx_nodes) // Rewrite `mdx-jsx` back to standard `element` nodes for downstream plugins
128719
128811
  .use(sanitize ? rehypeStripTags : undefined) // Strip STRIPPED_TAG_NAMES elements; this pipeline has no sanitization step
128720
128812
  .use(rehypeFlattenTableCellParagraphs) // Remove <p> wrappers inside table cells to prevent margin issues
@@ -128920,36 +129012,6 @@ const makeUseMDXComponents = (more = {}) => {
128920
129012
  };
128921
129013
  /* harmony default export */ const makeUseMdxComponents = (makeUseMDXComponents);
128922
129014
 
128923
- ;// ./lib/utils/mdxish/mdxish-variables.ts
128924
-
128925
-
128926
- // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
128927
- // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
128928
- // which either evaluated already or is meant to stay literal.
128929
- const MDX_VARIABLE_REGEX = new RegExp(`(?<!\\$)${variable_dist.MDX_VARIABLE_REGEXP}`, 'gu');
128930
- // Bracket notation names the same variable as dot notation, so normalize it before substituting.
128931
- // Escaped and `$`-prefixed forms are left alone so a reference that stays literal keeps its source.
128932
- // A closing escape needs no lookahead: requiring a literal `]}` already rules out `]\}`.
128933
- const BRACKET_NOTATION_REGEX = /(?<![$\\])\{user\[['"](\w+)['"]\]\}/gu;
128934
- /**
128935
- * Resolve `{user.*}` in a JSX attribute value against the same `user` binding the rmdx engine gets,
128936
- * so both engines agree. Body text differs on empty values: `Variable` falls back to the default.
128937
- *
128938
- * Legacy `<<...>>` is valid inside a quoted attribute but deliberately left literal — attributes are
128939
- * an MDX surface, and `{user.*}` is the syntax authors use there.
128940
- */
128941
- function resolveAttributeVariables(value, user) {
128942
- if (!value.includes('{user'))
128943
- return value;
128944
- return value
128945
- .replace(BRACKET_NOTATION_REGEX, '{user.$1}')
128946
- .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
128947
- if (escapePrefix || escapeSuffix)
128948
- return source;
128949
- return stringifyVariableValue(user[name]);
128950
- });
128951
- }
128952
-
128953
129015
  ;// ./lib/utils/mdxish/mdxish-render-utils.tsx
128954
129016
 
128955
129017
 
@@ -128960,6 +129022,7 @@ function resolveAttributeVariables(value, user) {
128960
129022
 
128961
129023
 
128962
129024
 
129025
+
128963
129026
  /** Flatten CustomComponents into a component map for rehype-react */
128964
129027
  function exportComponentsForRehype(components) {
128965
129028
  const exported = Object.entries(components).reduce((memo, [tag, mod]) => {
@@ -128992,10 +129055,10 @@ function exportComponentsForRehype(components) {
128992
129055
  * deliberately leaves mermaid alone; resolving it again here would override that.
128993
129056
  */
128994
129057
  const CODE_TAG_NAMES = new Set(['code']);
128995
- /** Raw markup injected with `dangerouslySetInnerHTML` by HTMLBlock and Embed — never interpolate. */
128996
- const RAW_HTML_PROP_NAMES = new Set(['html']);
129058
+ const isImageAlt = (tagName, key) => tagName === 'img' && key === 'alt';
128997
129059
  /**
128998
- * 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
128999
129062
  * attributes are plain strings, so they are substituted here — at render time, so that a
129000
129063
  * server-parsed (user-agnostic) tree resolves against the current reader's variables.
129001
129064
  */
@@ -129005,7 +129068,7 @@ function resolveVariablesInProps(props, user, tagName) {
129005
129068
  const isDocumentContent = RAW_HTML_PROP_NAMES.has(key) || (isCodeTag && key === 'value');
129006
129069
  if (typeof value !== 'string' || isDocumentContent)
129007
129070
  return [key, value];
129008
- return [key, resolveAttributeVariables(value, user)];
129071
+ return [key, resolveAttributeVariables(value, user, isImageAlt(tagName, key))];
129009
129072
  });
129010
129073
  return Object.fromEntries(resolvedEntries);
129011
129074
  }
@@ -129754,7 +129817,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129754
129817
  /******/ // startup
129755
129818
  /******/ // Load entry module and return exports
129756
129819
  /******/ // This entry module used 'module' so it can't be inlined
129757
- /******/ let __webpack_exports__ = __webpack_require__(6831);
129820
+ /******/ let __webpack_exports__ = __webpack_require__(3975);
129758
129821
  /******/ module.exports = __webpack_exports__;
129759
129822
  /******/
129760
129823
  /******/ })()