@mintlify/common 1.0.1115 → 1.0.1116

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.
@@ -3,6 +3,7 @@ import { remarkMdxExpandExpressions, } from './plugins/remark/remarkMdxExpandExp
3
3
  import { remarkMdxRemoveUnknownJsx } from './plugins/remark/remarkMdxRemoveUnknownJsx/index.js';
4
4
  import { remarkMermaid } from './plugins/remark/remarkMermaid.js';
5
5
  import { remarkNoCopyLanguage } from './plugins/remark/remarkNoCopyLanguage.js';
6
+ import { remarkParseTitleMarkdown, remarkRenderTitleMarkdown, } from './plugins/remark/remarkTitleMarkdown.js';
6
7
  // avoid running extractors unnecessarily
7
8
  const rehypeExtractors = (mdxExtracts, data) => {
8
9
  if (!mdxExtracts)
@@ -30,6 +31,7 @@ export const getMDXOptions = ({ data, remarkPlugins = [], rehypePlugins = [], md
30
31
  ],
31
32
  remarkMdxStyleStringToObject,
32
33
  [remarkResolveRelativeLinks, { path: data.path }],
34
+ remarkParseTitleMarkdown,
33
35
  remarkPrompt,
34
36
  [remarkComponentIds, data.pageMetadata],
35
37
  remarkUnwrapJsxHeadings,
@@ -46,6 +48,7 @@ export const getMDXOptions = ({ data, remarkPlugins = [], rehypePlugins = [], md
46
48
  remarkNoCopyLanguage,
47
49
  remarkVideo,
48
50
  ...remarkPlugins,
51
+ remarkRenderTitleMarkdown,
49
52
  remarkMapExplicitAnchors,
50
53
  [remarkMdxRemoveUnknownJsx, { allowlist: [...allowedComponents, ...knownComponents] }],
51
54
  [remarkMdxClientComponentBoundaries, { allowlist: knownComponents }],
@@ -1,8 +1,12 @@
1
1
  import type { RootContent as HastRootContent } from 'hast';
2
2
  import type { RootContent as MdastRootContent } from 'mdast';
3
3
  import type { MdxJsxAttribute, MdxJsxAttributeValueExpression } from 'mdast-util-mdx-jsx';
4
+ import type { Data } from 'unist';
4
5
  type RootContent = MdastRootContent | HastRootContent;
5
6
  export declare const createMdxJsxAttribute: (key: string, value: MdxJsxAttributeValueExpression | string | null | undefined | number | boolean) => MdxJsxAttribute;
7
+ export declare function getTitleMarkdownText(node: {
8
+ data?: Data;
9
+ }, fallback: string): string;
6
10
  export declare function getUnicodeId(title: string): string;
7
11
  export declare const getTableOfContentsTitle: (node: RootContent, index?: number, children?: RootContent[]) => string;
8
12
  export {};
@@ -7,6 +7,13 @@ export const createMdxJsxAttribute = (key, value) => {
7
7
  value: value,
8
8
  };
9
9
  };
10
+ export function getTitleMarkdownText(node, fallback) {
11
+ const nodeData = node.data;
12
+ const data = nodeData && 'mintlifyTitleMarkdown' in nodeData ? nodeData.mintlifyTitleMarkdown : undefined;
13
+ return data && typeof data === 'object' && 'text' in data && typeof data.text === 'string'
14
+ ? data.text
15
+ : fallback;
16
+ }
10
17
  export function getUnicodeId(title) {
11
18
  return encodeURIComponent(title.toLowerCase().trim().replace(/\s+/g, '-'));
12
19
  }
@@ -1,8 +1,7 @@
1
1
  import defaultSlugify, { slugifyWithCounter } from '@sindresorhus/slugify';
2
2
  import { visit } from 'unist-util-visit';
3
3
  import { slugify } from '../../../slugify.js';
4
- import { createMdxJsxAttribute } from '../../lib/remark-utils.js';
5
- import { getTableOfContentsTitle } from '../../lib/remark-utils.js';
4
+ import { createMdxJsxAttribute, getTableOfContentsTitle, getTitleMarkdownText, } from '../../lib/remark-utils.js';
6
5
  export function generateComponentId(title, count) {
7
6
  const base = defaultSlugify(title.replace(':', '-'), { decamelize: false });
8
7
  return count != null && count > 0 ? `${base}-${count}` : base;
@@ -65,14 +64,15 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
65
64
  else {
66
65
  const title = (_b = node.attributes.find((attr) => 'name' in attr && attr.name === 'title')) === null || _b === void 0 ? void 0 : _b.value;
67
66
  if (title && typeof title === 'string') {
67
+ const visibleTitle = getTitleMarkdownText(node, title);
68
68
  // Re-slug (bumping the counter) past any id an explicit prop claimed.
69
69
  // usedTabIds only guards against explicit ids; uniqueness among
70
70
  // title-derived slugs is owned by tabSlugifyFn, which monotonically
71
71
  // advances and never repeats a value, so the slots burned by this
72
72
  // loop don't need to be tracked here.
73
- let slug = slugify(title, tabSlugifyFn);
73
+ let slug = slugify(visibleTitle, tabSlugifyFn);
74
74
  while (usedTabIds.has(slug))
75
- slug = slugify(title, tabSlugifyFn);
75
+ slug = slugify(visibleTitle, tabSlugifyFn);
76
76
  usedTabIds.add(slug);
77
77
  tabSlugs.set(node, slug);
78
78
  }
@@ -164,7 +164,7 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
164
164
  const hasId = node.attributes.some((attr) => 'name' in attr && attr.name === 'id');
165
165
  if (hasId)
166
166
  return;
167
- const title = titleAttr.value;
167
+ const title = getTitleMarkdownText(node, titleAttr.value);
168
168
  const baseSlug = generateComponentId(title);
169
169
  const count = (_a = accordionCounts.get(baseSlug)) !== null && _a !== void 0 ? _a : 0;
170
170
  accordionCounts.set(baseSlug, count + 1);
@@ -1,6 +1,6 @@
1
1
  import { visit } from 'unist-util-visit';
2
2
  import { safeCleanHeadingId, slugify } from '../../../slugify.js';
3
- import { createMdxJsxAttribute, getTableOfContentsTitle } from '../../lib/remark-utils.js';
3
+ import { createMdxJsxAttribute, getTableOfContentsTitle, getTitleMarkdownText, } from '../../lib/remark-utils.js';
4
4
  import { isMdxJsxFragment } from '../../utils.js';
5
5
  import { AVOIDED_PAGE_MODES, HEADING_LEVELS } from './remarkComponentIds.js';
6
6
  import { isHiddenFromHumans, isVisibilityElement, resolveVisibilityFor, } from './remarkVisibilityForMarkdown.js';
@@ -95,7 +95,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
95
95
  if (node.type === 'mdxJsxFlowElement' && node.name === 'Tab') {
96
96
  const idValue = getStringAttribute(node.attributes, 'id');
97
97
  const titleValue = getStringAttribute(node.attributes, 'title');
98
- const tabId = idValue !== null && idValue !== void 0 ? idValue : (titleValue ? slugify(titleValue) : undefined);
98
+ const tabId = idValue !== null && idValue !== void 0 ? idValue : (titleValue ? slugify(getTitleMarkdownText(node, titleValue)) : undefined);
99
99
  if (tabId) {
100
100
  visit(node, (childNode) => {
101
101
  tabContentMap.set(childNode, tabId);
@@ -123,7 +123,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
123
123
  }
124
124
  });
125
125
  visit(tree, (node) => {
126
- var _a, _b, _c, _d, _e, _f, _g, _h;
126
+ var _a, _b, _c, _d, _e, _f;
127
127
  if (excludedNodes.has(node))
128
128
  return;
129
129
  const currentTabId = tabContentMap.get(node);
@@ -144,14 +144,17 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
144
144
  (attr.value == null ||
145
145
  attr.value === 'true' ||
146
146
  (typeof attr.value === 'object' && attr.value.value === 'true')));
147
+ const stepTitle = node.type === 'mdxJsxFlowElement' && node.name === 'Step'
148
+ ? getTitleMarkdownText(node, (_c = getStringAttribute(node.attributes, 'title')) !== null && _c !== void 0 ? _c : '')
149
+ : undefined;
147
150
  const isValidStep = node.type === 'mdxJsxFlowElement' &&
148
151
  node.name === 'Step' &&
149
152
  !hasNoAnchor &&
150
153
  ['h2', 'h3'].includes(stepTitleSize !== null && stepTitleSize !== void 0 ? stepTitleSize : '') &&
151
- ((_d = (_c = getStringAttribute(node.attributes, 'title')) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : '') !== '';
154
+ (stepTitle === null || stepTitle === void 0 ? void 0 : stepTitle.trim()) !== '';
152
155
  const hasIdAttribute = node.type === 'mdxJsxFlowElement' &&
153
156
  node.attributes.some((attr) => 'name' in attr && attr.name === 'id');
154
- const isAvoidedPageMode = AVOIDED_PAGE_MODES.includes((_e = pageMetadata === null || pageMetadata === void 0 ? void 0 : pageMetadata.mode) !== null && _e !== void 0 ? _e : '');
157
+ const isAvoidedPageMode = AVOIDED_PAGE_MODES.includes((_d = pageMetadata === null || pageMetadata === void 0 ? void 0 : pageMetadata.mode) !== null && _d !== void 0 ? _d : '');
155
158
  if (!isValidHeading &&
156
159
  !isValidMdxHeading &&
157
160
  !isTransformedHeading &&
@@ -169,7 +172,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
169
172
  }
170
173
  else if ('name' in node && node.name === 'Step') {
171
174
  const titleSizeToLevel = { h2: 2, h3: 3, h4: 4 };
172
- level = (_f = titleSizeToLevel[stepTitleSize !== null && stepTitleSize !== void 0 ? stepTitleSize : '']) !== null && _f !== void 0 ? _f : 3;
175
+ level = (_e = titleSizeToLevel[stepTitleSize !== null && stepTitleSize !== void 0 ? stepTitleSize : '']) !== null && _e !== void 0 ? _e : 3;
173
176
  // @ts-expect-error we're assigning to depth despite the node not containing depth in the type
174
177
  node.depth = level;
175
178
  }
@@ -182,7 +185,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
182
185
  level = levelAttr.value;
183
186
  }
184
187
  }
185
- else if ('name' in node && ((_g = node.name) === null || _g === void 0 ? void 0 : _g[1])) {
188
+ else if ('name' in node && ((_f = node.name) === null || _f === void 0 ? void 0 : _f[1])) {
186
189
  const num = Number(node.name[1]);
187
190
  level = !isNaN(num) ? num : undefined;
188
191
  if (level !== undefined) {
@@ -190,13 +193,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
190
193
  node.depth = level;
191
194
  }
192
195
  }
193
- let title;
194
- if ('name' in node && node.name === 'Step') {
195
- title = (_h = getStringAttribute(node.attributes, 'title')) !== null && _h !== void 0 ? _h : getTableOfContentsTitle(node);
196
- }
197
- else {
198
- title = getTableOfContentsTitle(node);
199
- }
196
+ const title = stepTitle !== null && stepTitle !== void 0 ? stepTitle : getTableOfContentsTitle(node);
200
197
  let slug;
201
198
  const explicitId = ('name' in node &&
202
199
  (node.name === 'Heading' || node.name === 'Step' || node.name === 'Update')) ||
@@ -0,0 +1,3 @@
1
+ import type { Root } from 'mdast';
2
+ export declare const remarkParseTitleMarkdown: () => (tree: Root) => void;
3
+ export declare const remarkRenderTitleMarkdown: () => (tree: Root) => void;
@@ -0,0 +1,83 @@
1
+ import { Parser } from 'acorn';
2
+ import jsx from 'acorn-jsx';
3
+ import { visit } from 'unist-util-visit';
4
+ import { parseTitleMarkdown, titleMarkdownToText } from '../../titleMarkdown.js';
5
+ const JSXParser = Parser.extend(jsx());
6
+ const TITLE_COMPONENTS = new Set(['Accordion', 'Card', 'Color.Row', 'Step', 'Tab', 'Tile']);
7
+ const TITLE_DATA_KEY = 'mintlifyTitleMarkdown';
8
+ function nodeJsx(node) {
9
+ if (node.type === 'text')
10
+ return `{${JSON.stringify(node.value)}}`;
11
+ if (node.type === 'inlineCode')
12
+ return `<code>{${JSON.stringify(node.value)}}</code>`;
13
+ if (!('children' in node))
14
+ return '';
15
+ const children = node.children.map(nodeJsx).join('');
16
+ if (node.type === 'link')
17
+ return children;
18
+ const tag = node.type === 'emphasis' ? 'em' : node.type === 'delete' ? 'del' : 'strong';
19
+ return `<${tag}${tag === 'strong' ? ' className="font-extrabold"' : ''}>${children}</${tag}>`;
20
+ }
21
+ function titleExpression(value) {
22
+ var _a;
23
+ const nodes = parseTitleMarkdown(value);
24
+ if (!nodes.length || (((_a = nodes[0]) === null || _a === void 0 ? void 0 : _a.type) === 'text' && nodes[0].value === value))
25
+ return;
26
+ const jsxValue = `<>${nodes.map(nodeJsx).join('')}</>`;
27
+ const estree = JSXParser.parse(jsxValue, {
28
+ ecmaVersion: 'latest',
29
+ sourceType: 'module',
30
+ });
31
+ return {
32
+ type: 'mdxJsxAttributeValueExpression',
33
+ value: jsxValue,
34
+ data: { estree },
35
+ };
36
+ }
37
+ function getTitleAttribute(node) {
38
+ return node.attributes.find((attribute) => attribute.type === 'mdxJsxAttribute' && attribute.name === 'title');
39
+ }
40
+ function getTitleValue(attribute) {
41
+ var _a, _b, _c;
42
+ if (typeof attribute.value === 'string')
43
+ return attribute.value;
44
+ const statement = (_c = (_b = (_a = attribute.value) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.estree) === null || _c === void 0 ? void 0 : _c.body[0];
45
+ return (statement === null || statement === void 0 ? void 0 : statement.type) === 'ExpressionStatement' &&
46
+ statement.expression.type === 'Literal' &&
47
+ typeof statement.expression.value === 'string'
48
+ ? statement.expression.value
49
+ : undefined;
50
+ }
51
+ export const remarkParseTitleMarkdown = () => (tree) => {
52
+ visit(tree, (node) => {
53
+ if (node.type !== 'mdxJsxFlowElement' && node.type !== 'mdxJsxTextElement')
54
+ return;
55
+ if (!node.name || !TITLE_COMPONENTS.has(node.name))
56
+ return;
57
+ const title = getTitleAttribute(node);
58
+ if (!title)
59
+ return;
60
+ const titleValue = getTitleValue(title);
61
+ if (titleValue === undefined)
62
+ return;
63
+ node.data = Object.assign(Object.assign({}, node.data), { [TITLE_DATA_KEY]: {
64
+ text: titleMarkdownToText(titleValue),
65
+ } });
66
+ });
67
+ };
68
+ export const remarkRenderTitleMarkdown = () => (tree) => {
69
+ visit(tree, (node) => {
70
+ var _a;
71
+ if (node.type !== 'mdxJsxFlowElement' && node.type !== 'mdxJsxTextElement')
72
+ return;
73
+ if (!node.name || !TITLE_COMPONENTS.has(node.name))
74
+ return;
75
+ const title = getTitleAttribute(node);
76
+ if (!title)
77
+ return;
78
+ const titleValue = getTitleValue(title);
79
+ if (titleValue === undefined)
80
+ return;
81
+ title.value = (_a = titleExpression(titleValue)) !== null && _a !== void 0 ? _a : title.value;
82
+ });
83
+ };
@@ -0,0 +1,3 @@
1
+ import type { PhrasingContent } from 'mdast';
2
+ export declare function parseTitleMarkdown(value: string): PhrasingContent[];
3
+ export declare function titleMarkdownToText(value: string): string;
@@ -0,0 +1,90 @@
1
+ import { fromMarkdown } from 'mdast-util-from-markdown';
2
+ import { gfmFromMarkdown } from 'mdast-util-gfm';
3
+ import { gfm } from 'micromark-extension-gfm';
4
+ import { getTableOfContentsTitle } from './lib/remark-utils.js';
5
+ function sourceText(node, source) {
6
+ var _a, _b;
7
+ const start = (_a = node.position) === null || _a === void 0 ? void 0 : _a.start.offset;
8
+ const end = (_b = node.position) === null || _b === void 0 ? void 0 : _b.end.offset;
9
+ return typeof start === 'number' && typeof end === 'number' ? source.slice(start, end) : '';
10
+ }
11
+ function appendText(nodes, value) {
12
+ if (!value)
13
+ return;
14
+ const previous = nodes.at(-1);
15
+ if ((previous === null || previous === void 0 ? void 0 : previous.type) === 'text')
16
+ previous.value += value;
17
+ else
18
+ nodes.push({ type: 'text', value });
19
+ }
20
+ function isSupportedLink(node, source) {
21
+ const raw = sourceText(node, source);
22
+ const destination = node.url.trim();
23
+ return (raw.startsWith('[') &&
24
+ destination.length > 0 &&
25
+ (/^(?:https?:\/\/|mailto:|tel:|\/|#|\.\.?\/)/i.test(destination) ||
26
+ destination.includes('/') ||
27
+ /^[^\s@]+\.[^\s]+$/u.test(destination)));
28
+ }
29
+ function sanitizeNodes(nodes, source) {
30
+ const sanitized = [];
31
+ for (const node of nodes) {
32
+ switch (node.type) {
33
+ case 'text':
34
+ appendText(sanitized, node.value.replace(/\n/g, ' '));
35
+ break;
36
+ case 'inlineCode':
37
+ sanitized.push({ type: 'inlineCode', value: node.value });
38
+ break;
39
+ case 'strong':
40
+ case 'emphasis':
41
+ case 'delete':
42
+ sanitized.push(Object.assign(Object.assign({}, node), { children: sanitizeNodes(node.children, source) }));
43
+ break;
44
+ case 'link':
45
+ if (isSupportedLink(node, source)) {
46
+ sanitized.push(Object.assign(Object.assign({}, node), { children: sanitizeNodes(node.children, source) }));
47
+ }
48
+ else {
49
+ appendText(sanitized, sourceText(node, source));
50
+ }
51
+ break;
52
+ default:
53
+ appendText(sanitized, sourceText(node, source));
54
+ }
55
+ }
56
+ return sanitized;
57
+ }
58
+ export function parseTitleMarkdown(value) {
59
+ if (!value)
60
+ return [];
61
+ try {
62
+ const prefix = 'title: ';
63
+ const source = `${prefix}${value.replace(/\n/g, ' ')}`;
64
+ const root = fromMarkdown(source, {
65
+ extensions: [gfm()],
66
+ mdastExtensions: [gfmFromMarkdown()],
67
+ });
68
+ const paragraph = root.children.length === 1 ? root.children[0] : undefined;
69
+ if ((paragraph === null || paragraph === void 0 ? void 0 : paragraph.type) !== 'paragraph')
70
+ return [{ type: 'text', value }];
71
+ const nodes = sanitizeNodes(paragraph.children, source);
72
+ const first = nodes[0];
73
+ if ((first === null || first === void 0 ? void 0 : first.type) !== 'text' || !first.value.startsWith(prefix)) {
74
+ return [{ type: 'text', value }];
75
+ }
76
+ first.value = first.value.slice(prefix.length);
77
+ if (!first.value)
78
+ nodes.shift();
79
+ return nodes;
80
+ }
81
+ catch (_a) {
82
+ return [{ type: 'text', value }];
83
+ }
84
+ }
85
+ export function titleMarkdownToText(value) {
86
+ return getTableOfContentsTitle({
87
+ type: 'paragraph',
88
+ children: parseTitleMarkdown(value),
89
+ });
90
+ }