@mintlify/common 1.0.1114 → 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.
- package/dist/mdx/getMDXOptions.js +3 -0
- package/dist/mdx/lib/remark-utils.d.ts +4 -0
- package/dist/mdx/lib/remark-utils.js +7 -0
- package/dist/mdx/plugins/rehype/rehypeDynamicTailwindCss.js +30 -31
- package/dist/mdx/plugins/rehype/segment.d.ts +13 -0
- package/dist/mdx/plugins/rehype/segment.js +94 -0
- package/dist/mdx/plugins/remark/remarkComponentIds.js +5 -5
- package/dist/mdx/plugins/remark/remarkExtractTableOfContents.js +11 -14
- package/dist/mdx/plugins/remark/remarkTitleMarkdown.d.ts +3 -0
- package/dist/mdx/plugins/remark/remarkTitleMarkdown.js +83 -0
- package/dist/mdx/titleMarkdown.d.ts +3 -0
- package/dist/mdx/titleMarkdown.js +90 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -2
|
@@ -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,22 +1,21 @@
|
|
|
1
1
|
import { walk } from 'estree-walker';
|
|
2
2
|
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { segment } from './segment.js';
|
|
3
4
|
export const MINTLIFY_TAILWIND_PREFIX = 'mint-';
|
|
4
5
|
export const rehypeDynamicTailwindCss = (tailwindSelectors) => (tree) => {
|
|
6
|
+
const normalizedTailwindSelectors = tailwindSelectors === null || tailwindSelectors === void 0 ? void 0 : tailwindSelectors.map(normalizeSelector);
|
|
5
7
|
visit(tree, (node) => {
|
|
6
8
|
if ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
|
|
7
9
|
node.name &&
|
|
8
10
|
typeof node.name === 'string' &&
|
|
9
|
-
// check for valid html tags
|
|
10
|
-
(/^[a-z]/.test(node.name) || node.name === 'Heading') &&
|
|
11
|
-
!node.name.includes('.') &&
|
|
12
11
|
node.attributes.length > 0) {
|
|
13
12
|
const className = node.attributes.find((attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'className');
|
|
14
13
|
if (className && typeof className.value === 'string') {
|
|
15
|
-
className.value = transformClassNames(className.value,
|
|
14
|
+
className.value = transformClassNames(className.value, normalizedTailwindSelectors);
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
if (node.type === 'mdxjsEsm' && node.data && node.data.estree) {
|
|
19
|
-
transformMdxJsEsmNode(node.data.estree,
|
|
18
|
+
transformMdxJsEsmNode(node.data.estree, normalizedTailwindSelectors);
|
|
20
19
|
}
|
|
21
20
|
});
|
|
22
21
|
};
|
|
@@ -93,31 +92,31 @@ const transformExpressionClassNames = (expr, tailwindSelectors) => {
|
|
|
93
92
|
const transformClassNames = (value, tailwindSelectors) => {
|
|
94
93
|
if (!tailwindSelectors || !tailwindSelectors.length)
|
|
95
94
|
return value;
|
|
96
|
-
return value
|
|
97
|
-
.
|
|
98
|
-
.map((className) => {
|
|
99
|
-
if (!className)
|
|
100
|
-
return '';
|
|
101
|
-
if (className.startsWith('[') || className.startsWith(MINTLIFY_TAILWIND_PREFIX)) {
|
|
102
|
-
return className;
|
|
103
|
-
}
|
|
104
|
-
const parts = className.split(':');
|
|
105
|
-
let baseClass = parts.pop();
|
|
106
|
-
const hasImportantModifier = baseClass.startsWith('!');
|
|
107
|
-
if (hasImportantModifier) {
|
|
108
|
-
baseClass = baseClass.slice(1);
|
|
109
|
-
}
|
|
110
|
-
const variants = parts;
|
|
111
|
-
if (tailwindSelectors.some((selector) => {
|
|
112
|
-
// remove the leading dot and any escaping backslashes from the selector
|
|
113
|
-
// for example, .bg-orange-400\\/50 -> bg-orange-400/50
|
|
114
|
-
const normalizedSelector = selector.replace(/^\.|\\+/g, '');
|
|
115
|
-
return normalizedSelector.includes(baseClass);
|
|
116
|
-
})) {
|
|
117
|
-
const transformedClass = `${hasImportantModifier ? '!' : ''}${MINTLIFY_TAILWIND_PREFIX}${baseClass}`;
|
|
118
|
-
return [...variants, transformedClass].join(':');
|
|
119
|
-
}
|
|
120
|
-
return className;
|
|
121
|
-
})
|
|
95
|
+
return splitClassNames(value)
|
|
96
|
+
.map((className) => transformClassName(className, tailwindSelectors))
|
|
122
97
|
.join(' ');
|
|
123
98
|
};
|
|
99
|
+
const transformClassName = (className, tailwindSelectors) => {
|
|
100
|
+
const classSegments = splitClassSegments(className);
|
|
101
|
+
const baseClassSegment = classSegments[classSegments.length - 1];
|
|
102
|
+
if (!baseClassSegment)
|
|
103
|
+
return className;
|
|
104
|
+
const hasImportantModifier = baseClassSegment.startsWith('!');
|
|
105
|
+
const baseClass = hasImportantModifier ? baseClassSegment.slice(1) : baseClassSegment;
|
|
106
|
+
if (!baseClass || baseClass.startsWith(MINTLIFY_TAILWIND_PREFIX)) {
|
|
107
|
+
return className;
|
|
108
|
+
}
|
|
109
|
+
if (!isTailwindClass(className, tailwindSelectors)) {
|
|
110
|
+
return className;
|
|
111
|
+
}
|
|
112
|
+
classSegments[classSegments.length - 1] =
|
|
113
|
+
`${hasImportantModifier ? '!' : ''}${MINTLIFY_TAILWIND_PREFIX}${baseClass}`;
|
|
114
|
+
return classSegments.join(':');
|
|
115
|
+
};
|
|
116
|
+
const isTailwindClass = (className, tailwindSelectors) => {
|
|
117
|
+
const normalizedClassName = normalizeSelector(className);
|
|
118
|
+
return tailwindSelectors.some((selector) => selector.includes(`.${normalizedClassName}`));
|
|
119
|
+
};
|
|
120
|
+
const splitClassNames = (value) => value.split(/\s+/).filter(Boolean);
|
|
121
|
+
const splitClassSegments = (value) => segment(value, ':').filter(Boolean);
|
|
122
|
+
const normalizeSelector = (selector) => selector.replace(/\\+/g, '');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This splits a string on a top-level character.
|
|
3
|
+
*
|
|
4
|
+
* Regex doesn't support recursion (at least not the JS-flavored version),
|
|
5
|
+
* so we have to use a tiny state machine to keep track of paren placement.
|
|
6
|
+
*
|
|
7
|
+
* Expected behavior using commas:
|
|
8
|
+
* var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
|
|
9
|
+
* ┬ ┬ ┬ ┬
|
|
10
|
+
* x x x ╰──────── Split because top-level
|
|
11
|
+
* ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
|
|
12
|
+
*/
|
|
13
|
+
export declare function segment(input: string, separator: string): string[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Vendored from Tailwind CSS v4 (MIT), unchanged apart from formatting:
|
|
2
|
+
// https://github.com/tailwindlabs/tailwindcss/blob/main/packages/tailwindcss/src/utils/segment.ts
|
|
3
|
+
const BACKSLASH = 0x5c;
|
|
4
|
+
const OPEN_CURLY = 0x7b;
|
|
5
|
+
const CLOSE_CURLY = 0x7d;
|
|
6
|
+
const OPEN_PAREN = 0x28;
|
|
7
|
+
const CLOSE_PAREN = 0x29;
|
|
8
|
+
const OPEN_BRACKET = 0x5b;
|
|
9
|
+
const CLOSE_BRACKET = 0x5d;
|
|
10
|
+
const DOUBLE_QUOTE = 0x22;
|
|
11
|
+
const SINGLE_QUOTE = 0x27;
|
|
12
|
+
// This is a shared buffer that is used to keep track of the current nesting level
|
|
13
|
+
// of parens, brackets, and braces. It is used to determine if a character is at
|
|
14
|
+
// the top-level of a string. This is a performance optimization to avoid memory
|
|
15
|
+
// allocations on every call to `segment`.
|
|
16
|
+
const closingBracketStack = new Uint8Array(256);
|
|
17
|
+
/**
|
|
18
|
+
* This splits a string on a top-level character.
|
|
19
|
+
*
|
|
20
|
+
* Regex doesn't support recursion (at least not the JS-flavored version),
|
|
21
|
+
* so we have to use a tiny state machine to keep track of paren placement.
|
|
22
|
+
*
|
|
23
|
+
* Expected behavior using commas:
|
|
24
|
+
* var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
|
|
25
|
+
* ┬ ┬ ┬ ┬
|
|
26
|
+
* x x x ╰──────── Split because top-level
|
|
27
|
+
* ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
|
|
28
|
+
*/
|
|
29
|
+
export function segment(input, separator) {
|
|
30
|
+
// SAFETY: We can use an index into a shared buffer because this function is
|
|
31
|
+
// synchronous, non-recursive, and runs in a single-threaded environment.
|
|
32
|
+
let stackPos = 0;
|
|
33
|
+
const parts = [];
|
|
34
|
+
let lastPos = 0;
|
|
35
|
+
const len = input.length;
|
|
36
|
+
const separatorCode = separator.charCodeAt(0);
|
|
37
|
+
for (let idx = 0; idx < len; idx++) {
|
|
38
|
+
const char = input.charCodeAt(idx);
|
|
39
|
+
if (stackPos === 0 && char === separatorCode) {
|
|
40
|
+
parts.push(input.slice(lastPos, idx));
|
|
41
|
+
lastPos = idx + 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
switch (char) {
|
|
45
|
+
case BACKSLASH:
|
|
46
|
+
// The next character is escaped, so we skip it.
|
|
47
|
+
idx += 1;
|
|
48
|
+
break;
|
|
49
|
+
// Strings should be handled as-is until the end of the string. No need to
|
|
50
|
+
// worry about balancing parens, brackets, or curlies inside a string.
|
|
51
|
+
case SINGLE_QUOTE:
|
|
52
|
+
case DOUBLE_QUOTE:
|
|
53
|
+
// Ensure we don't go out of bounds.
|
|
54
|
+
while (++idx < len) {
|
|
55
|
+
const nextChar = input.charCodeAt(idx);
|
|
56
|
+
// The next character is escaped, so we skip it.
|
|
57
|
+
if (nextChar === BACKSLASH) {
|
|
58
|
+
idx += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (nextChar === char) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case OPEN_PAREN:
|
|
67
|
+
closingBracketStack[stackPos] = CLOSE_PAREN;
|
|
68
|
+
stackPos++;
|
|
69
|
+
break;
|
|
70
|
+
case OPEN_BRACKET:
|
|
71
|
+
closingBracketStack[stackPos] = CLOSE_BRACKET;
|
|
72
|
+
stackPos++;
|
|
73
|
+
break;
|
|
74
|
+
case OPEN_CURLY:
|
|
75
|
+
closingBracketStack[stackPos] = CLOSE_CURLY;
|
|
76
|
+
stackPos++;
|
|
77
|
+
break;
|
|
78
|
+
case CLOSE_BRACKET:
|
|
79
|
+
case CLOSE_CURLY:
|
|
80
|
+
case CLOSE_PAREN:
|
|
81
|
+
if (stackPos > 0 && char === closingBracketStack[stackPos - 1]) {
|
|
82
|
+
// SAFETY: The buffer does not need to be mutated because the stack is
|
|
83
|
+
// only ever read from or written to its current position. Its current
|
|
84
|
+
// position is only ever incremented after writing to it. Meaning that
|
|
85
|
+
// the buffer can be dirty for the next use and still be correct since
|
|
86
|
+
// reading/writing always starts at position `0`.
|
|
87
|
+
stackPos--;
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
parts.push(input.slice(lastPos));
|
|
93
|
+
return parts;
|
|
94
|
+
}
|
|
@@ -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(
|
|
73
|
+
let slug = slugify(visibleTitle, tabSlugifyFn);
|
|
74
74
|
while (usedTabIds.has(slug))
|
|
75
|
-
slug = slugify(
|
|
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
|
|
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
|
-
(
|
|
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((
|
|
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 = (
|
|
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 && ((
|
|
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
|
-
|
|
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,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,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
|
+
}
|