@mintlify/common 1.0.1269 → 1.0.1270

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.
Files changed (26) hide show
  1. package/dist/mdx/estreeReferences.d.ts +39 -0
  2. package/dist/mdx/estreeReferences.js +963 -0
  3. package/dist/mdx/plugins/remark/firstPartyMarkdownComponents.d.ts +3 -0
  4. package/dist/mdx/plugins/remark/firstPartyMarkdownComponents.js +29 -0
  5. package/dist/mdx/plugins/remark/remarkMdxRemoveUnknownJsx/index.d.ts +1 -1
  6. package/dist/mdx/plugins/remark/remarkVisibilityForMarkdown.d.ts +7 -1
  7. package/dist/mdx/plugins/remark/remarkVisibilityForMarkdown.js +581 -177
  8. package/dist/mdx/plugins/remark/staticEvaluator.d.ts +55 -0
  9. package/dist/mdx/plugins/remark/staticEvaluator.js +639 -0
  10. package/dist/mdx/plugins/remark/unfoldComponentsForMarkdown.d.ts +71 -0
  11. package/dist/mdx/plugins/remark/unfoldComponentsForMarkdown.js +1414 -0
  12. package/dist/mdx/remark-core.d.ts +7 -0
  13. package/dist/mdx/remark-core.js +32 -0
  14. package/dist/mdx/remark.d.ts +1 -7
  15. package/dist/mdx/remark.js +2 -32
  16. package/dist/mdx/snippets/getExportMap.js +33 -5
  17. package/dist/mdx/snippets/resolveAllImports.js +3 -2
  18. package/dist/mdx/snippets/resolveImport/exportCollision.d.ts +1 -0
  19. package/dist/mdx/snippets/resolveImport/exportCollision.js +2 -1
  20. package/dist/mdx/snippets/resolveImport/index.d.ts +11 -1
  21. package/dist/mdx/snippets/resolveImport/index.js +129 -16
  22. package/dist/mdx/snippets/resolveImport/resolveComponentWithContent.js +38 -5
  23. package/dist/mdx/snippets/resolveImport/snippetDeclarations.d.ts +56 -0
  24. package/dist/mdx/snippets/resolveImport/snippetDeclarations.js +655 -0
  25. package/dist/tsconfig.build.tsbuildinfo +1 -1
  26. package/package.json +2 -2
@@ -1,4 +1,9 @@
1
1
  import { WILDCARD_GROUP, checkGroupAccess, isPageGroups } from '../../../navigation/groupAccess.js';
2
+ import { isObject } from '../../../types/guards.js';
3
+ import { getAST, stringifyTree } from '../../remark-core.js';
4
+ import { BLOCK_LINK_LABEL } from './firstPartyMarkdownComponents.js';
5
+ import { evaluateStatic } from './staticEvaluator.js';
6
+ import { chargeUnfoldBudget, chargeUnfoldCall, collectMarkdownComponentContext, collectUsageProps, containsJsx, createUnfoldBudget, definitionGatesContent, DROP_ELEMENTS, dropPairedDarkOnly, escapeInterpolatedText, HEADING_ELEMENTS, isPresentationalAttribute, UNWRAP_BLOCK_ELEMENTS, UNWRAP_INLINE_ELEMENTS, escapeLinkLabel, extractProgramExpression, markdownLinkDestination, MAX_UNFOLD_DEPTH, GFM_ALERT_PATTERN, HEADLINE_ATTRIBUTES, KEEP_TAG_COMPONENTS, materializeComponent, serializeExpressionJsx, } from './unfoldComponentsForMarkdown.js';
2
7
  export function isVisibilityElement(node) {
3
8
  return ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
4
9
  node.name === 'Visibility');
@@ -6,6 +11,19 @@ export function isVisibilityElement(node) {
6
11
  function isMdxJsxElement(node) {
7
12
  return node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement';
8
13
  }
14
+ /** `{}` and `{/* comment *\/}` compile to nothing, so they are not children. */
15
+ function isEmptyChild(node) {
16
+ var _a;
17
+ if (node.type === 'text')
18
+ return node.value.trim() === '';
19
+ if (node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') {
20
+ const estree = (_a = node.data) === null || _a === void 0 ? void 0 : _a.estree;
21
+ return estree === undefined || estree === null || estree.body.length === 0;
22
+ }
23
+ if (node.type === 'paragraph')
24
+ return node.children.every((child) => isEmptyChild(child));
25
+ return false;
26
+ }
9
27
  export const VISIBILITY_AUDIENCES = ['humans', 'agents', 'signed-in', 'signed-out'];
10
28
  export const VISIBILITY_FOR_EXPECTED = '"humans", "agents", "signed-in", "signed-out", or { groups: [...] }';
11
29
  export function isVisibilityAudience(value) {
@@ -60,7 +78,7 @@ function toVisibilityGroups(value) {
60
78
  return rule && isPageGroups([rule]) ? rule : undefined;
61
79
  }
62
80
  export function toVisibilityGroupsRule(value) {
63
- if (!isRecord(value) || Object.keys(value).length !== 1)
81
+ if (!isObject(value) || Object.keys(value).length !== 1)
64
82
  return undefined;
65
83
  const groups = toVisibilityGroups(value.groups);
66
84
  return groups && { groups };
@@ -123,15 +141,21 @@ function mayOverrideFor(attr) {
123
141
  return (key !== null && key !== void 0 ? key : literalKey) === 'for';
124
142
  });
125
143
  }
126
- export function resolveVisibilityFor(node) {
127
- var _a, _b;
144
+ /**
145
+ * Resolves a `Visibility` audience. With `literalScope`, an evaluated audience that shows content
146
+ * to agents must resolve the same from literal values alone; otherwise it is dynamic, since an
147
+ * object it was read from may have been changed by a write the evaluator cannot see.
148
+ */
149
+ export function resolveVisibilityFor(node, scope = new Map(), literalScope) {
150
+ var _a, _b, _c;
128
151
  let invalidRaw;
129
152
  let sawDynamic = false;
130
153
  let sawAttribute = false;
131
154
  const lastFor = node.attributes.findLastIndex((attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'for');
132
155
  if (node.attributes.slice(lastFor + 1).some(mayOverrideFor))
133
156
  return { kind: 'dynamic' };
134
- for (const attr of node.attributes) {
157
+ // JSX applies the last `for`, so earlier ones never take effect
158
+ for (const attr of lastFor === -1 ? [] : node.attributes.slice(lastFor, lastFor + 1)) {
135
159
  if (attr.type !== 'mdxJsxAttribute' || attr.name !== 'for')
136
160
  continue;
137
161
  sawAttribute = true;
@@ -172,6 +196,28 @@ export function resolveVisibilityFor(node) {
172
196
  continue;
173
197
  }
174
198
  }
199
+ const expression = extractProgramExpression('data' in attr.value ? (_c = attr.value.data) === null || _c === void 0 ? void 0 : _c.estree : undefined);
200
+ const evaluated = evaluateStatic(expression, { scope });
201
+ if (evaluated.ok) {
202
+ const value = evaluated.value;
203
+ if (isVisibilityAudience(value)) {
204
+ const resolution = { kind: 'audience', audience: value };
205
+ if (literalScope === undefined || isHiddenFromAgents(resolution))
206
+ return resolution;
207
+ const literal = evaluateStatic(expression, { scope: literalScope });
208
+ if (literal.ok && literal.value === value)
209
+ return resolution;
210
+ sawDynamic = true;
211
+ continue;
212
+ }
213
+ const rule = toVisibilityGroupsRule(value);
214
+ if (rule)
215
+ return { kind: 'groups', groups: rule.groups };
216
+ if (typeof value === 'string') {
217
+ invalidRaw !== null && invalidRaw !== void 0 ? invalidRaw : (invalidRaw = value);
218
+ continue;
219
+ }
220
+ }
175
221
  sawDynamic = true;
176
222
  }
177
223
  }
@@ -224,221 +270,579 @@ export function matchesVisibilityAuthRule(rule, viewer) {
224
270
  return !viewer.signedIn;
225
271
  return checkGroupAccess(toPageGroups(rule.groups), new Set(viewer.signedIn ? [...viewer.groups, WILDCARD_GROUP] : []));
226
272
  }
227
- // `export * from` is intentionally ignored: it exposes no statically-knowable
228
- // names and creates no page-local JSX bindings in MDX, so there is nothing to
229
- // collect.
230
- function collectEsmIdentifiers(node) {
231
- var _a, _b, _c;
232
- const names = new Set();
233
- if (node.type !== 'mdxjsEsm')
234
- return names;
235
- for (const statement of (_c = (_b = (_a = node.data) === null || _a === void 0 ? void 0 : _a.estree) === null || _b === void 0 ? void 0 : _b.body) !== null && _c !== void 0 ? _c : []) {
236
- if (statement.type === 'ImportDeclaration') {
237
- for (const specifier of statement.specifiers) {
238
- names.add(specifier.local.name);
239
- }
273
+ function transformChildren(children, ctx) {
274
+ return dropPairedDarkOnly(children, (child) => usageClassName(child, ctx)).flatMap((child) => transformNode(child, ctx));
275
+ }
276
+ function usageClassName(node, ctx) {
277
+ var _a;
278
+ if (!isMdxJsxElement(node))
279
+ return undefined;
280
+ const hasClass = node.attributes.some((attr) => attr.type === 'mdxJsxAttribute' && (attr.name === 'className' || attr.name === 'class'));
281
+ if (!hasClass)
282
+ return undefined;
283
+ const props = collectUsageProps(node, ctx);
284
+ return (_a = props.get('className')) !== null && _a !== void 0 ? _a : props.get('class');
285
+ }
286
+ const BLOCK_NODE_TYPES = new Set([
287
+ 'blockquote',
288
+ 'code',
289
+ 'heading',
290
+ 'list',
291
+ 'table',
292
+ 'thematicBreak',
293
+ 'definition',
294
+ 'footnoteDefinition',
295
+ 'mdxJsxFlowElement',
296
+ 'mdxFlowExpression',
297
+ 'mdxjsEsm',
298
+ 'yaml',
299
+ 'toml',
300
+ 'math',
301
+ ]);
302
+ function wrapPhrasingRuns(nodes) {
303
+ const result = [];
304
+ let run = [];
305
+ const flush = () => {
306
+ if (run.length === 0)
307
+ return;
308
+ const paragraph = { type: 'paragraph', children: [] };
309
+ replaceChildren(paragraph.children, run);
310
+ result.push(paragraph);
311
+ run = [];
312
+ };
313
+ for (const node of nodes) {
314
+ if (BLOCK_NODE_TYPES.has(node.type) || node.type === 'paragraph' || node.type === 'html') {
315
+ flush();
316
+ result.push(node);
240
317
  }
241
- if (statement.type === 'ExportNamedDeclaration') {
242
- if (statement.declaration) {
243
- if (statement.declaration.type === 'VariableDeclaration') {
244
- for (const declarator of statement.declaration.declarations) {
245
- if (declarator.id.type === 'Identifier') {
246
- names.add(declarator.id.name);
247
- }
248
- }
249
- }
250
- else if (statement.declaration.id) {
251
- names.add(statement.declaration.id.name);
252
- }
253
- }
254
- // Re-exports: `export { Panel } from '...'` keep names in specifiers
255
- for (const specifier of statement.specifiers) {
256
- if (specifier.local.type === 'Identifier') {
257
- names.add(specifier.local.name);
258
- }
259
- if (specifier.exported.type === 'Identifier') {
260
- names.add(specifier.exported.name);
261
- }
262
- }
318
+ else {
319
+ run.push(node);
320
+ }
321
+ }
322
+ flush();
323
+ return result;
324
+ }
325
+ const PHRASING_NODE_TYPES = new Set([
326
+ 'text',
327
+ 'emphasis',
328
+ 'strong',
329
+ 'delete',
330
+ 'inlineCode',
331
+ 'inlineMath',
332
+ 'break',
333
+ 'link',
334
+ 'image',
335
+ 'linkReference',
336
+ 'imageReference',
337
+ 'footnoteReference',
338
+ 'html',
339
+ 'mdxJsxTextElement',
340
+ 'mdxTextExpression',
341
+ ]);
342
+ function isPhrasingContent(node) {
343
+ return PHRASING_NODE_TYPES.has(node.type);
344
+ }
345
+ function flattenToPhrasing(nodes) {
346
+ const result = [];
347
+ const pushGroup = (children) => {
348
+ if (children.length === 0)
349
+ return;
350
+ if (result.length > 0)
351
+ result.push({ type: 'text', value: ' ' });
352
+ result.push(...children);
353
+ };
354
+ for (const node of nodes) {
355
+ if (node.type === 'paragraph' || node.type === 'heading') {
356
+ pushGroup([...node.children]);
357
+ }
358
+ else if (node.type === 'blockquote') {
359
+ pushGroup(flattenToPhrasing([...node.children]));
360
+ }
361
+ else if (node.type === 'mdxJsxFlowElement') {
362
+ // Kept tags such as View carry selectors, so the tag stays and only its content flattens
363
+ pushGroup([
364
+ {
365
+ type: 'mdxJsxTextElement',
366
+ name: node.name,
367
+ attributes: node.attributes,
368
+ children: flattenToPhrasing([...node.children]).filter(isPhrasingContent),
369
+ },
370
+ ]);
371
+ }
372
+ else if (node.type === 'list') {
373
+ for (const item of node.children)
374
+ pushGroup(flattenToPhrasing([...item.children]));
375
+ }
376
+ else if (node.type === 'code') {
377
+ pushGroup([{ type: 'inlineCode', value: node.value }]);
263
378
  }
264
- if (statement.type === 'ExportDefaultDeclaration') {
265
- const declaration = statement.declaration;
266
- if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') &&
267
- declaration.id) {
268
- names.add(declaration.id.name);
379
+ else if (node.type === 'table') {
380
+ for (const row of node.children) {
381
+ for (const cell of row.children)
382
+ pushGroup([...cell.children]);
269
383
  }
270
384
  }
385
+ else if (!BLOCK_NODE_TYPES.has(node.type)) {
386
+ result.push(node);
387
+ }
271
388
  }
272
- return names;
389
+ return result;
273
390
  }
274
- function isRecord(value) {
275
- return typeof value === 'object' && value !== null;
391
+ /**
392
+ * When a usage's body is one static expression, such as `<Check>{0}</Check>`, `children` is that
393
+ * value rather than its markdown text, so `children ? … : …` takes the branch React would.
394
+ */
395
+ function bodyExpressionValue(bodyChildren, ctx) {
396
+ var _a;
397
+ let only = bodyChildren.length === 1 ? bodyChildren[0] : undefined;
398
+ if ((only === null || only === void 0 ? void 0 : only.type) === 'paragraph') {
399
+ const meaningful = only.children.filter((child) => !isEmptyChild(child));
400
+ only = meaningful.length === 1 ? meaningful[0] : undefined;
401
+ }
402
+ if ((only === null || only === void 0 ? void 0 : only.type) !== 'mdxFlowExpression' && (only === null || only === void 0 ? void 0 : only.type) !== 'mdxTextExpression')
403
+ return undefined;
404
+ const evaluated = evaluateStatic(extractProgramExpression((_a = only.data) === null || _a === void 0 ? void 0 : _a.estree), {
405
+ scope: ctx.moduleScope,
406
+ });
407
+ if (!evaluated.ok || (typeof evaluated.value === 'object' && evaluated.value !== null)) {
408
+ return undefined;
409
+ }
410
+ return { value: evaluated.value };
276
411
  }
277
- function getJsxReferenceBaseName(name) {
278
- let current = name;
279
- while (isRecord(current) && current.type === 'JSXMemberExpression') {
280
- current = current.object;
412
+ function unfoldUsage(node, name, ctx) {
413
+ var _a;
414
+ const def = ctx.defs.get(name);
415
+ if (!def)
416
+ return undefined;
417
+ const withinLimits = !ctx.stack.includes(def) && ctx.stack.length < MAX_UNFOLD_DEPTH && chargeUnfoldCall(ctx);
418
+ if (!withinLimits)
419
+ return definitionGatesContent(def, ctx.defs) ? [] : undefined;
420
+ const bodyChildren = node.children.filter((child) => !isEmptyChild(child));
421
+ const transformedChildren = transformChildren(bodyChildren, ctx);
422
+ const childrenRoot = { type: 'root', children: transformedChildren };
423
+ const childrenMarkdown = transformedChildren.length > 0 ? stringifyTree(childrenRoot).trim() : '';
424
+ const failedProps = new Set();
425
+ const props = collectUsageProps(node, ctx, failedProps);
426
+ // Body children replace any children attribute, even when they render nothing
427
+ if (bodyChildren.length > 0) {
428
+ props.delete('children');
429
+ failedProps.delete('children');
281
430
  }
282
- if (isRecord(current) && current.type === 'JSXIdentifier' && typeof current.name === 'string') {
283
- return current.name;
431
+ const trustedFailed = new Set();
432
+ const trustedProps = collectUsageProps(node, Object.assign(Object.assign({}, ctx), { moduleScope: ctx.literalScope }), trustedFailed);
433
+ if (bodyChildren.length > 0)
434
+ trustedProps.delete('children');
435
+ const markdown = materializeComponent(def, name, props, childrenMarkdown, ctx, failedProps, bodyExpressionValue(bodyChildren, ctx), { props: trustedProps, failedProps: trustedFailed });
436
+ if (markdown === undefined) {
437
+ if (definitionGatesContent(def, ctx.defs))
438
+ return [];
439
+ // Same fallback as inside definitions: headline and description props still render
440
+ return ((_a = unfoldForeignUsage(node, name, ctx, transformedChildren)) !== null && _a !== void 0 ? _a : wrapPhrasingRuns(transformedChildren));
441
+ }
442
+ if (markdown.trim() === '')
443
+ return [];
444
+ if (!chargeUnfoldBudget(ctx, markdown.length)) {
445
+ return definitionGatesContent(def, ctx.defs) ? [] : wrapPhrasingRuns(transformedChildren);
446
+ }
447
+ const innerCtx = Object.assign(Object.assign({}, ctx), { stack: [...ctx.stack, def] });
448
+ return parseMarkdown(markdown, innerCtx);
449
+ }
450
+ function parseMarkdown(markdown, ctx) {
451
+ try {
452
+ // Unfolded output is never real ESM; a line that reads like `import` or `export` is text
453
+ const children = getAST(markdown).children.map((child) => child.type === 'mdxjsEsm'
454
+ ? { type: 'paragraph', children: [{ type: 'text', value: child.value }] }
455
+ : child);
456
+ return transformChildren(children, ctx);
457
+ }
458
+ catch (_a) {
459
+ return [{ type: 'text', value: markdown }];
460
+ }
461
+ }
462
+ /** The first of `names` whose prop is a string with visible text. */
463
+ function firstTextProp(props, names) {
464
+ for (const name of names) {
465
+ const value = props.get(name);
466
+ if (typeof value === 'string' && value.trim() !== '')
467
+ return value;
284
468
  }
285
469
  return undefined;
286
470
  }
287
- // Walks an estree for JSX component references, e.g. `<Logo />` within
288
- // `export const Header = () => <Logo />` or `{<Demo />}` expressions.
289
- function collectEstreeJsxNames(estree, names) {
290
- const queue = [estree];
291
- const seen = new Set();
292
- while (queue.length > 0) {
293
- const current = queue.pop();
294
- if (Array.isArray(current)) {
295
- queue.push(...current);
296
- continue;
297
- }
298
- if (!isRecord(current) || seen.has(current))
299
- continue;
300
- seen.add(current);
301
- if (current.type === 'JSXOpeningElement') {
302
- const baseName = getJsxReferenceBaseName(current.name);
303
- // Lowercase JSX names are intrinsic elements, never component bindings.
304
- if (baseName && /^[A-Z]/.test(baseName))
305
- names.add(baseName);
471
+ /** A linked headline when the component has an `href`, and a bold one otherwise. */
472
+ function headlineMarkdown(title, href) {
473
+ const label = escapeInterpolatedText(title);
474
+ if (typeof href !== 'string' || href === '')
475
+ return `**${label}**`;
476
+ return `[${escapeLinkLabel(label)}](${markdownLinkDestination(href)})`;
477
+ }
478
+ function unfoldForeignUsage(node, name, ctx, transformed) {
479
+ if (KEEP_TAG_COMPONENTS.has(name))
480
+ return undefined;
481
+ const props = collectUsageProps(node, ctx);
482
+ if (props.get('aria-hidden') === true || props.get('aria-hidden') === 'true')
483
+ return [];
484
+ const result = [];
485
+ const title = firstTextProp(props, HEADLINE_ATTRIBUTES);
486
+ if (title !== undefined) {
487
+ result.push(...parseMarkdown(headlineMarkdown(title, props.get('href')), ctx));
488
+ }
489
+ const transformedChildren = transformed !== null && transformed !== void 0 ? transformed : transformChildren([...node.children], ctx);
490
+ const hasChildContent = transformedChildren.some((child) => !(child.type === 'text' && child.value.trim() === ''));
491
+ if (hasChildContent) {
492
+ result.push(...transformedChildren);
493
+ }
494
+ else {
495
+ const childrenProp = props.get('children');
496
+ const body = typeof childrenProp === 'number'
497
+ ? String(childrenProp)
498
+ : firstTextProp(props, ['children', 'description', 'alt']);
499
+ if (body !== undefined)
500
+ result.push(...parseMarkdown(escapeInterpolatedText(body), ctx));
501
+ }
502
+ if (result.length === 0) {
503
+ const ariaLabel = firstTextProp(props, ['aria-label']);
504
+ if (ariaLabel !== undefined)
505
+ return parseMarkdown(escapeInterpolatedText(ariaLabel), ctx);
506
+ }
507
+ return wrapPhrasingRuns(result);
508
+ }
509
+ function unfoldEmbeddedFrame(node, ctx) {
510
+ const props = collectUsageProps(node, ctx);
511
+ if (props.get('aria-hidden') === true || props.get('aria-hidden') === 'true')
512
+ return [];
513
+ const rawLabel = firstTextProp(props, ['title', 'aria-label']);
514
+ const src = props.get('src');
515
+ if (typeof src === 'string' && src !== '') {
516
+ const label = escapeInterpolatedText(rawLabel !== null && rawLabel !== void 0 ? rawLabel : src);
517
+ return parseMarkdown(`[${escapeLinkLabel(label)}](${markdownLinkDestination(src)})`, ctx);
518
+ }
519
+ return rawLabel === undefined ? [] : parseMarkdown(escapeInterpolatedText(rawLabel), ctx);
520
+ }
521
+ const PHRASING_WRAPPERS = {
522
+ strong: 'strong',
523
+ b: 'strong',
524
+ em: 'emphasis',
525
+ i: 'emphasis',
526
+ del: 'delete',
527
+ s: 'delete',
528
+ };
529
+ /** HTML written in the page becomes markdown; tags with no markdown form stay without styling. */
530
+ function transformIntrinsicElement(node, name, ctx) {
531
+ const props = collectUsageProps(node, ctx);
532
+ if (props.get('aria-hidden') === true || props.get('aria-hidden') === 'true')
533
+ return [];
534
+ const fallbackLabel = accessibleLabel(props);
535
+ const fallback = () => fallbackLabel === '' ? [] : [{ type: 'text', value: fallbackLabel }];
536
+ if (DROP_ELEMENTS.has(name))
537
+ return fallback();
538
+ if (name === 'br')
539
+ return node.type === 'mdxJsxTextElement' ? [{ type: 'break' }] : [];
540
+ if (name === 'hr')
541
+ return [{ type: 'thematicBreak' }];
542
+ if (name === 'img') {
543
+ const src = props.get('src');
544
+ const alt = props.get('alt');
545
+ if (typeof src === 'string' && src !== '') {
546
+ return [{ type: 'image', url: src, alt: typeof alt === 'string' ? alt : '' }];
306
547
  }
307
- queue.push(...Object.values(current));
548
+ return typeof alt === 'string' && alt !== '' ? [{ type: 'text', value: alt }] : [];
549
+ }
550
+ const children = transformChildren([...node.children], ctx);
551
+ const phrasing = () => flattenToPhrasing(children).filter(isPhrasingContent);
552
+ if (UNWRAP_BLOCK_ELEMENTS.has(name) || UNWRAP_INLINE_ELEMENTS.has(name)) {
553
+ return children.length > 0 ? children : fallback();
554
+ }
555
+ const depth = HEADING_ELEMENTS[name];
556
+ if (depth !== undefined) {
557
+ const text = phrasing();
558
+ return text.length > 0 ? [{ type: 'heading', depth, children: text }] : [];
559
+ }
560
+ const wrapper = PHRASING_WRAPPERS[name];
561
+ if (wrapper !== undefined) {
562
+ const text = phrasing();
563
+ return text.length > 0 ? [{ type: wrapper, children: text }] : [];
564
+ }
565
+ if (name === 'code') {
566
+ const value = textContent(children);
567
+ return value === '' ? [] : [{ type: 'inlineCode', value }];
568
+ }
569
+ if (name === 'blockquote') {
570
+ if (children.length === 0)
571
+ return fallback();
572
+ const blockquote = { type: 'blockquote', children: [] };
573
+ replaceChildren(blockquote.children, wrapPhrasingRuns(children));
574
+ return [blockquote];
308
575
  }
576
+ if (name === 'a') {
577
+ const href = props.get('href');
578
+ if (typeof href !== 'string' || href === '')
579
+ return children.length > 0 ? children : fallback();
580
+ return linkContent(children, href, fallbackLabel);
581
+ }
582
+ node.attributes = node.attributes.filter((attr) => attr.type !== 'mdxJsxAttribute' || !isPresentationalAttribute(attr.name));
583
+ replaceChildren(node.children, children);
584
+ return [node];
309
585
  }
310
- // JSX rendered inside an ESM node's own declarations, so stripping `Header`
311
- // can also pull in helpers that only exist to serve it.
312
- function collectJsxReferences(node) {
313
- var _a;
314
- const names = new Set();
315
- if (node.type === 'mdxjsEsm') {
316
- collectEstreeJsxNames((_a = node.data) === null || _a === void 0 ? void 0 : _a.estree, names);
586
+ function isBlockHtmlElement(name) {
587
+ return (HEADING_ELEMENTS[name] !== undefined ||
588
+ UNWRAP_BLOCK_ELEMENTS.has(name) ||
589
+ name === 'blockquote' ||
590
+ name === 'hr');
591
+ }
592
+ function accessibleLabel(props) {
593
+ for (const key of ['aria-label', 'title']) {
594
+ const value = props.get(key);
595
+ if (typeof value === 'string' && value.trim() !== '')
596
+ return value;
317
597
  }
318
- return names;
598
+ return '';
319
599
  }
320
- // Classifies every JSX component usage on the page: names used only inside
321
- // `<Visibility for="humans">` land in `hiddenNames`, everything else in
322
- // `visibleNames`. Usage inside an agents block counts as visible since that
323
- // content survives into the markdown output.
324
- function collectJsxUsage(nodes, insideHumans, hiddenNames, visibleNames) {
325
- var _a, _b, _c;
600
+ /**
601
+ * A link label holds only inline content, so a link around blocks moves onto their first
602
+ * heading, or follows them as its own line.
603
+ */
604
+ function linkContent(children, url, accessible) {
605
+ const blocks = wrapPhrasingRuns(children);
606
+ const [first] = blocks;
607
+ if (blocks.length === 0 || (blocks.length === 1 && (first === null || first === void 0 ? void 0 : first.type) === 'paragraph')) {
608
+ const text = flattenToPhrasing(children).filter(isPhrasingContent);
609
+ const label = accessible === '' ? url : accessible;
610
+ return [
611
+ { type: 'link', url, children: text.length > 0 ? text : [{ type: 'text', value: label }] },
612
+ ];
613
+ }
614
+ const label = accessible === '' ? BLOCK_LINK_LABEL : accessible;
615
+ const heading = blocks.find((block) => block.type === 'heading');
616
+ if ((heading === null || heading === void 0 ? void 0 : heading.type) === 'heading') {
617
+ heading.children = [{ type: 'link', url, children: heading.children }];
618
+ return blocks;
619
+ }
620
+ return [
621
+ ...blocks,
622
+ {
623
+ type: 'paragraph',
624
+ children: [{ type: 'link', url, children: [{ type: 'text', value: label }] }],
625
+ },
626
+ ];
627
+ }
628
+ function textContent(nodes) {
629
+ let text = '';
326
630
  for (const node of nodes) {
327
- let childInsideHumans = insideHumans;
328
- if (isMdxJsxElement(node)) {
329
- if (node.name === 'Visibility') {
330
- const resolution = resolveVisibilityFor(node);
331
- if (isHiddenFromAgents(resolution))
332
- childInsideHumans = true;
333
- }
334
- else {
335
- const baseName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.split('.')[0];
336
- // Lowercase JSX names are intrinsic elements, never component bindings.
337
- if (baseName && /^[A-Z]/.test(baseName)) {
338
- (insideHumans ? hiddenNames : visibleNames).add(baseName);
339
- }
340
- }
631
+ if (node.type === 'text' || node.type === 'inlineCode')
632
+ text += node.value;
633
+ else if ('children' in node)
634
+ text += textContent(node.children);
635
+ }
636
+ return text;
637
+ }
638
+ function unfoldComponentElement(node, name, ctx) {
639
+ var _a;
640
+ if (name === 'Visibility')
641
+ return undefined;
642
+ const baseName = name.split('.')[0];
643
+ if (baseName === undefined || !/^[A-Z]/.test(baseName))
644
+ return undefined;
645
+ const element = name === 'Steps' ? withStepDefaults(node, ctx) : node;
646
+ return (_a = unfoldUsage(element, name, ctx)) !== null && _a !== void 0 ? _a : unfoldForeignUsage(element, name, ctx);
647
+ }
648
+ /** `Steps` numbers its `Step` children and passes its `titleSize` down; a Step's own props win. */
649
+ function withStepDefaults(node, ctx) {
650
+ const titleSize = collectUsageProps(node, ctx).get('titleSize');
651
+ let index = 0;
652
+ const number = (child) => {
653
+ // A one-line `<Step>` parses as inline, inside a paragraph of its own, and Steps flattens
654
+ // fragments before numbering
655
+ if (child.type === 'paragraph')
656
+ return Object.assign(Object.assign({}, child), { children: child.children.map(number) });
657
+ if (isMdxJsxElement(child) && child.name === null) {
658
+ return Object.assign(Object.assign({}, child), { children: child.children.map(number) });
341
659
  }
342
- // Expression-embedded usages like `{<Demo />}` live in estree, not mdast.
343
- if (node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') {
344
- collectEstreeJsxNames((_b = node.data) === null || _b === void 0 ? void 0 : _b.estree, insideHumans ? hiddenNames : visibleNames);
660
+ // Agent markdown unwraps a Visibility agents can see, so its Steps keep counting; one hidden
661
+ // from agents is dropped and takes no number
662
+ if (isMdxJsxElement(child) && child.name === 'Visibility') {
663
+ const resolution = resolveVisibilityFor(child, ctx.moduleScope, ctx.literalScope);
664
+ if (resolution.kind === 'invalid' || isHiddenFromAgents(resolution))
665
+ return child;
666
+ return Object.assign(Object.assign({}, child), { children: child.children.map(number) });
345
667
  }
346
- // Attribute-embedded usages like `<Card icon={<Logo />}>` also live in estree.
347
- if (isMdxJsxElement(node)) {
348
- for (const attr of node.attributes) {
349
- const expression = attr.type === 'mdxJsxAttribute' ? attr.value : attr;
350
- if (expression && typeof expression === 'object' && 'data' in expression) {
351
- collectEstreeJsxNames((_c = expression.data) === null || _c === void 0 ? void 0 : _c.estree, insideHumans ? hiddenNames : visibleNames);
352
- }
353
- }
668
+ if (!isMdxJsxElement(child) || child.name !== 'Step')
669
+ return child;
670
+ index += 1;
671
+ const defaults = [
672
+ { type: 'mdxJsxAttribute', name: 'stepNumber', value: String(index) },
673
+ ];
674
+ if (typeof titleSize === 'string') {
675
+ defaults.push({ type: 'mdxJsxAttribute', name: 'titleSize', value: titleSize });
354
676
  }
355
- if ('children' in node && Array.isArray(node.children)) {
356
- collectJsxUsage(node.children, childInsideHumans, hiddenNames, visibleNames);
677
+ return Object.assign(Object.assign({}, child), { attributes: [...defaults, ...child.attributes] });
678
+ };
679
+ return Object.assign(Object.assign({}, node), { children: node.children.map(number) });
680
+ }
681
+ function withinUnfoldBudget(ctx, cost) {
682
+ if (cost > ctx.budget.remainingBytes)
683
+ return false;
684
+ return chargeUnfoldBudget(ctx, cost);
685
+ }
686
+ function transformExpression(node, ctx) {
687
+ var _a;
688
+ const expression = extractProgramExpression((_a = node.data) === null || _a === void 0 ? void 0 : _a.estree);
689
+ if (expression === undefined)
690
+ return undefined;
691
+ const jsx = serializeExpressionJsx(expression, ctx);
692
+ if (jsx !== undefined) {
693
+ const trimmed = jsx.trim();
694
+ if (trimmed === '')
695
+ return [];
696
+ if (!withinUnfoldBudget(ctx, trimmed.length))
697
+ return undefined;
698
+ try {
699
+ return transformChildren(getAST(trimmed).children, ctx);
357
700
  }
701
+ catch (_b) {
702
+ return [{ type: 'text', value: trimmed }];
703
+ }
704
+ }
705
+ const evaluated = evaluateStatic(expression, { scope: ctx.moduleScope });
706
+ if (evaluated.ok) {
707
+ const value = evaluated.value;
708
+ if (value === null || value === undefined || typeof value === 'boolean')
709
+ return [];
710
+ if (typeof value === 'object')
711
+ return [];
712
+ const text = String(value);
713
+ if (!withinUnfoldBudget(ctx, text.length))
714
+ return undefined;
715
+ return [{ type: 'text', value: text }];
358
716
  }
717
+ // Kept as source, the expression would print `Visibility` copy agents should not see
718
+ if (containsJsx(expression, 'Visibility'))
719
+ return [];
720
+ return undefined;
359
721
  }
360
- function transformChildren(children, esmToStrip) {
361
- return children.flatMap((child) => transformNode(child, esmToStrip));
722
+ function promoteComponentOnlyParagraph(node, ctx) {
723
+ const meaningful = dropPairedDarkOnly(node.children.filter((child) => !(child.type === 'text' && child.value.trim() === '')), (child) => usageClassName(child, ctx));
724
+ if (meaningful.length === 0)
725
+ return undefined;
726
+ const result = [];
727
+ for (const child of meaningful) {
728
+ // A one-line `<h2>` or `<div>` parses as inline but stands for a block; inline tags stay in
729
+ // their paragraph so the spaces between them survive
730
+ if (child.type === 'mdxJsxTextElement' && typeof child.name === 'string') {
731
+ if (isBlockHtmlElement(child.name)) {
732
+ result.push(...transformIntrinsicElement(child, child.name, ctx));
733
+ continue;
734
+ }
735
+ if (/^[a-z]/.test(child.name))
736
+ return undefined;
737
+ }
738
+ if (child.type === 'mdxJsxTextElement' &&
739
+ typeof child.name === 'string' &&
740
+ child.name !== 'Visibility') {
741
+ const unfolded = unfoldComponentElement(child, child.name, ctx);
742
+ if (unfolded === undefined)
743
+ return undefined;
744
+ result.push(...unfolded);
745
+ continue;
746
+ }
747
+ if (child.type === 'mdxTextExpression') {
748
+ const transformed = transformExpression(child, ctx);
749
+ if (transformed === undefined)
750
+ return undefined;
751
+ result.push(...transformed);
752
+ continue;
753
+ }
754
+ return undefined;
755
+ }
756
+ return wrapPhrasingRuns(result);
362
757
  }
363
- function transformNode(node, esmToStrip) {
364
- // Snippet definitions are only stripped when every usage of the components
365
- // they declare sits inside a `<Visibility for="humans">` block; JSX that the
366
- // author left visible keeps its definition in the markdown output.
758
+ function transformNode(node, ctx) {
367
759
  if (node.type === 'mdxjsEsm') {
368
- return esmToStrip.has(node) ? [] : [node];
760
+ return [];
761
+ }
762
+ // A fragment renders only its children
763
+ if (isMdxJsxElement(node) && node.name === null) {
764
+ const unwrapped = transformChildren([...node.children], ctx);
765
+ return node.type === 'mdxJsxTextElement' ? flattenToPhrasing(unwrapped) : unwrapped;
369
766
  }
370
767
  if (isMdxJsxElement(node) && node.name === 'Visibility') {
371
- const resolution = resolveVisibilityFor(node);
768
+ const resolution = resolveVisibilityFor(node, ctx.moduleScope, ctx.literalScope);
372
769
  if (resolution.kind === 'invalid') {
373
770
  console.warn(`<Visibility for="${resolution.raw}"> has an unknown audience — expected ${VISIBILITY_FOR_EXPECTED}. The content was removed from the markdown output.`);
374
771
  return [];
375
772
  }
376
773
  if (isHiddenFromAgents(resolution))
377
774
  return [];
378
- if (resolution.kind === 'audience') {
379
- return transformChildren(node.children, esmToStrip);
775
+ return transformChildren(node.children, ctx);
776
+ }
777
+ if (isMdxJsxElement(node) && (node.name === 'iframe' || node.name === 'embed')) {
778
+ const unfolded = unfoldEmbeddedFrame(node, ctx);
779
+ return node.type === 'mdxJsxTextElement' ? flattenToPhrasing(unfolded) : unfolded;
780
+ }
781
+ if (isMdxJsxElement(node) && typeof node.name === 'string' && /^[a-z]/.test(node.name)) {
782
+ const converted = transformIntrinsicElement(node, node.name, ctx);
783
+ return node.type === 'mdxJsxTextElement'
784
+ ? flattenToPhrasing(converted)
785
+ : wrapPhrasingRuns(converted);
786
+ }
787
+ if (node.type === 'paragraph') {
788
+ const promoted = promoteComponentOnlyParagraph(node, ctx);
789
+ if (promoted !== undefined)
790
+ return promoted;
791
+ }
792
+ if (isMdxJsxElement(node) && typeof node.name === 'string' && node.name !== '') {
793
+ const unfolded = unfoldComponentElement(node, node.name, ctx);
794
+ if (unfolded !== undefined) {
795
+ return node.type === 'mdxJsxTextElement' ? flattenToPhrasing(unfolded) : unfolded;
380
796
  }
381
797
  }
798
+ if (node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') {
799
+ const transformed = transformExpression(node, ctx);
800
+ if (transformed === undefined)
801
+ return [node];
802
+ return node.type === 'mdxTextExpression'
803
+ ? flattenToPhrasing(transformed)
804
+ : wrapPhrasingRuns(transformed);
805
+ }
382
806
  if ('children' in node && Array.isArray(node.children)) {
383
- const next = transformChildren(node.children, esmToStrip);
384
- node.children = next;
807
+ replaceChildren(node.children, transformChildren([...node.children], ctx));
385
808
  }
386
809
  return [node];
387
810
  }
388
- export const remarkVisibilityForMarkdown = () => (tree) => {
389
- var _a, _b, _c, _d;
390
- const hiddenNames = new Set();
391
- const visibleNames = new Set();
392
- collectJsxUsage(tree.children, false, hiddenNames, visibleNames);
393
- const esmNodes = tree.children.filter((child) => child.type === 'mdxjsEsm');
394
- const declaredNames = new Map(esmNodes.map((node) => [node, [...collectEsmIdentifiers(node)]]));
395
- const referencedNames = new Map(esmNodes.map((node) => [node, [...collectJsxReferences(node)]]));
396
- const esmToStrip = new Set();
397
- // Keep-alive fixed point: a node with any visibly-used name keeps every
398
- // name it declares or renders visible, so a visible `<PanelAlias />` keeps
399
- // the `export const Panel = ...` node it aliases, and a kept `Footer` keeps
400
- // the `Logo` helper it renders, even when both live in separate ESM nodes.
401
- let visibleChanged = true;
402
- while (visibleChanged) {
403
- visibleChanged = false;
404
- for (const node of esmNodes) {
405
- const declared = (_a = declaredNames.get(node)) !== null && _a !== void 0 ? _a : [];
406
- if (!declared.some((name) => visibleNames.has(name)))
407
- continue;
408
- for (const name of [...declared, ...((_b = referencedNames.get(node)) !== null && _b !== void 0 ? _b : [])]) {
409
- if (!visibleNames.has(name)) {
410
- visibleNames.add(name);
411
- visibleChanged = true;
811
+ function replaceChildren(children, next) {
812
+ children.length = 0;
813
+ children.push(...next);
814
+ }
815
+ function normalizeGfmAlerts(nodes) {
816
+ for (const node of nodes) {
817
+ if (node.type === 'blockquote') {
818
+ const [first] = node.children;
819
+ if ((first === null || first === void 0 ? void 0 : first.type) === 'paragraph') {
820
+ const [firstChild] = first.children;
821
+ if ((firstChild === null || firstChild === void 0 ? void 0 : firstChild.type) === 'text') {
822
+ const match = GFM_ALERT_PATTERN.exec(firstChild.value);
823
+ if (match) {
824
+ const remainder = firstChild.value.slice(match[0].length).replace(/^\n/, '');
825
+ if (remainder === '') {
826
+ first.children.shift();
827
+ if (first.children.length === 0)
828
+ node.children.shift();
829
+ }
830
+ else {
831
+ firstChild.value = remainder;
832
+ }
833
+ node.children.unshift({ type: 'html', value: match[0] });
834
+ }
412
835
  }
413
836
  }
414
837
  }
415
- }
416
- // Fixed point: stripping a node marks everything it declares or renders as
417
- // hidden, so `export { Panel as PanelAlias }` pulls in the `export const
418
- // Panel = ...` it aliases, and a stripped `Header` pulls in the `Logo`
419
- // helper rendered only inside its body, instead of leaking their source.
420
- let changed = true;
421
- while (changed) {
422
- changed = false;
423
- for (const node of esmNodes) {
424
- if (esmToStrip.has(node))
425
- continue;
426
- const names = (_c = declaredNames.get(node)) !== null && _c !== void 0 ? _c : [];
427
- // Declared-but-never-rendered names don't block stripping: when a node
428
- // mixes a hidden component with unused names, hiding wins over leaking.
429
- const hiddenOnly = names.some((name) => hiddenNames.has(name)) &&
430
- !names.some((name) => visibleNames.has(name));
431
- if (!hiddenOnly)
432
- continue;
433
- esmToStrip.add(node);
434
- // Visible names stay out of hiddenNames so the two sets stay disjoint.
435
- for (const name of [...names, ...((_d = referencedNames.get(node)) !== null && _d !== void 0 ? _d : [])]) {
436
- if (!hiddenNames.has(name) && !visibleNames.has(name)) {
437
- hiddenNames.add(name);
438
- changed = true;
439
- }
440
- }
838
+ if ('children' in node && Array.isArray(node.children)) {
839
+ normalizeGfmAlerts([...node.children]);
441
840
  }
442
841
  }
443
- tree.children = transformChildren(tree.children, esmToStrip);
842
+ }
843
+ export const remarkVisibilityForMarkdown = () => (tree) => {
844
+ const componentContext = collectMarkdownComponentContext(tree);
845
+ const ctx = Object.assign(Object.assign({}, componentContext), { stack: [], budget: createUnfoldBudget() });
846
+ tree.children = transformChildren(tree.children, ctx);
847
+ normalizeGfmAlerts(tree.children);
444
848
  };