@mintlify/common 1.0.1032 → 1.0.1034
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/plugins/rehype/rehypeParamFieldIds.js +48 -20
- package/dist/mdx/plugins/remark/remarkComponentIds.js +23 -23
- package/dist/mdx/plugins/remark/remarkExtractChangelogFilters.js +13 -4
- package/dist/mdx/plugins/remark/remarkExtractTableOfContents.js +7 -3
- package/dist/mdx/plugins/remark/remarkMdxExtractPanel.js +83 -12
- package/dist/mdx/plugins/remark/remarkSplitTabs.js +9 -1
- package/dist/mdx/plugins/remark/remarkValidateTabs.js +15 -7
- package/dist/rss/index.js +13 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -4,31 +4,59 @@ export function generateParamFieldId(name, count) {
|
|
|
4
4
|
const suffix = count > 0 ? `_${count}` : '';
|
|
5
5
|
return slugify(`param-${name}${suffix}`, { decamelize: true, separator: '-' });
|
|
6
6
|
}
|
|
7
|
+
const PARAM_FIELD_COMPONENTS = ['ParamField', 'Param', 'ResponseField'];
|
|
8
|
+
const PARAM_NAME_PRECEDENCE = ['query', 'path', 'body', 'header'];
|
|
9
|
+
const getStringAttribute = (element, attrName) => {
|
|
10
|
+
const attr = element.attributes.find((attr) => 'name' in attr && attr.name === attrName);
|
|
11
|
+
return attr && typeof attr.value === 'string' && attr.value ? attr.value : undefined;
|
|
12
|
+
};
|
|
13
|
+
const hasIdAttribute = (element) => element.attributes.some((attr) => 'name' in attr && attr.name === 'id');
|
|
14
|
+
const getFieldName = (element) => {
|
|
15
|
+
if (element.name === 'ResponseField')
|
|
16
|
+
return getStringAttribute(element, 'name');
|
|
17
|
+
for (const attrName of PARAM_NAME_PRECEDENCE) {
|
|
18
|
+
const value = getStringAttribute(element, attrName);
|
|
19
|
+
if (value)
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
};
|
|
7
24
|
export const rehypeParamFieldIds = () => {
|
|
8
25
|
return (tree) => {
|
|
9
|
-
const
|
|
26
|
+
const claimedIds = new Set();
|
|
27
|
+
const slugCounts = new Map();
|
|
28
|
+
visit(tree, 'mdxJsxFlowElement', (element) => {
|
|
29
|
+
if (!element.name || !PARAM_FIELD_COMPONENTS.includes(element.name))
|
|
30
|
+
return;
|
|
31
|
+
if (!hasIdAttribute(element))
|
|
32
|
+
return;
|
|
33
|
+
const explicitId = getStringAttribute(element, 'id');
|
|
34
|
+
if (explicitId)
|
|
35
|
+
claimedIds.add(explicitId);
|
|
36
|
+
});
|
|
10
37
|
visit(tree, 'mdxJsxFlowElement', (element) => {
|
|
11
38
|
var _a;
|
|
12
|
-
if (element.name
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
element.attributes.push({
|
|
26
|
-
type: 'mdxJsxAttribute',
|
|
27
|
-
name: 'id',
|
|
28
|
-
value: id,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
39
|
+
if (!element.name || !PARAM_FIELD_COMPONENTS.includes(element.name))
|
|
40
|
+
return;
|
|
41
|
+
if (hasIdAttribute(element))
|
|
42
|
+
return;
|
|
43
|
+
const name = getFieldName(element);
|
|
44
|
+
if (!name)
|
|
45
|
+
return;
|
|
46
|
+
const baseSlug = generateParamFieldId(name, 0);
|
|
47
|
+
let count = (_a = slugCounts.get(baseSlug)) !== null && _a !== void 0 ? _a : 0;
|
|
48
|
+
let id = generateParamFieldId(name, count);
|
|
49
|
+
while (claimedIds.has(id)) {
|
|
50
|
+
count += 1;
|
|
51
|
+
id = generateParamFieldId(name, count);
|
|
31
52
|
}
|
|
53
|
+
slugCounts.set(baseSlug, count + 1);
|
|
54
|
+
claimedIds.add(id);
|
|
55
|
+
element.attributes.push({
|
|
56
|
+
type: 'mdxJsxAttribute',
|
|
57
|
+
name: 'id',
|
|
58
|
+
value: id,
|
|
59
|
+
});
|
|
32
60
|
});
|
|
33
61
|
return tree;
|
|
34
62
|
};
|
|
@@ -93,31 +93,31 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
|
|
|
93
93
|
const processTabsRecursively = (node) => {
|
|
94
94
|
if (node.type === 'mdxJsxFlowElement' && node.name === 'Tab') {
|
|
95
95
|
const slug = tabSlugs.get(node);
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
node.attributes.push(createMdxJsxAttribute('id', slug));
|
|
103
|
-
}
|
|
104
|
-
const childTabIds = [];
|
|
105
|
-
for (const child of node.children) {
|
|
106
|
-
const childIds = collectDirectChildIds(child, 'Tab');
|
|
107
|
-
childTabIds.push(...childIds);
|
|
108
|
-
}
|
|
109
|
-
if (childTabIds.length > 0) {
|
|
110
|
-
try {
|
|
111
|
-
node.attributes.push(createMdxJsxAttribute(CHILD_TAB_IDS_ATTRIBUTE, JSON.stringify(childTabIds)));
|
|
96
|
+
if (slug) {
|
|
97
|
+
// `slug` already equals the explicit `id` when one was provided, so only
|
|
98
|
+
// add the attribute when the Tab doesn't already have one.
|
|
99
|
+
const hasId = node.attributes.some((attr) => 'name' in attr && attr.name === 'id');
|
|
100
|
+
if (!hasId) {
|
|
101
|
+
node.attributes.push(createMdxJsxAttribute('id', slug));
|
|
112
102
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
103
|
+
const childTabIds = [];
|
|
104
|
+
for (const child of node.children) {
|
|
105
|
+
const childIds = collectDirectChildIds(child, 'Tab');
|
|
106
|
+
childTabIds.push(...childIds);
|
|
107
|
+
}
|
|
108
|
+
if (childTabIds.length > 0) {
|
|
109
|
+
try {
|
|
110
|
+
node.attributes.push(createMdxJsxAttribute(CHILD_TAB_IDS_ATTRIBUTE, JSON.stringify(childTabIds)));
|
|
111
|
+
}
|
|
112
|
+
catch (_a) { }
|
|
113
|
+
}
|
|
114
|
+
const childHeadingIds = collectDirectChildIds(node, 'Heading');
|
|
115
|
+
if (childHeadingIds.length > 0) {
|
|
116
|
+
try {
|
|
117
|
+
node.attributes.push(createMdxJsxAttribute(CHILD_HEADING_IDS_ATTRIBUTE, JSON.stringify(childHeadingIds)));
|
|
118
|
+
}
|
|
119
|
+
catch (_b) { }
|
|
119
120
|
}
|
|
120
|
-
catch (_b) { }
|
|
121
121
|
}
|
|
122
122
|
for (const child of node.children) {
|
|
123
123
|
processTabsRecursively(child);
|
|
@@ -21,11 +21,20 @@ export function remarkExtractChangelogFilters(mdxExtracts) {
|
|
|
21
21
|
if (!Array.isArray(tags)) {
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const nodeTags = new Set();
|
|
25
|
+
for (const tag of tags) {
|
|
26
|
+
if (typeof tag !== 'string') {
|
|
27
|
+
console.warn(`Invalid tag ${JSON.stringify(tag)} in <Update> tags — tags must be strings. Skipping it.`);
|
|
28
|
+
continue;
|
|
27
29
|
}
|
|
28
|
-
|
|
30
|
+
const trimmed = tag.trim();
|
|
31
|
+
if (trimmed) {
|
|
32
|
+
nodeTags.add(trimmed);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const tag of nodeTags) {
|
|
36
|
+
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
|
37
|
+
}
|
|
29
38
|
}
|
|
30
39
|
const filters = Array.from(tagCounts.entries())
|
|
31
40
|
.map(([tag, count]) => ({
|
|
@@ -28,9 +28,13 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
|
|
|
28
28
|
seenSlugs.set(slug, count + 1);
|
|
29
29
|
if (count === 0)
|
|
30
30
|
return slug;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
let suffix = count + 1;
|
|
32
|
+
let suffixed = `${slug}-${suffix}`;
|
|
33
|
+
while (seenSlugs.has(suffixed)) {
|
|
34
|
+
suffix += 1;
|
|
35
|
+
suffixed = `${slug}-${suffix}`;
|
|
36
|
+
}
|
|
37
|
+
seenSlugs.set(suffixed, 1);
|
|
34
38
|
return suffixed;
|
|
35
39
|
};
|
|
36
40
|
return (tree) => {
|
|
@@ -1,36 +1,107 @@
|
|
|
1
1
|
import remarkStringify from 'remark-stringify';
|
|
2
2
|
import { unified } from 'unified';
|
|
3
|
-
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { SKIP, visit } from 'unist-util-visit';
|
|
4
4
|
import { coreRemarkMdxPlugins } from '../../remark.js';
|
|
5
|
+
const COMMENT_ONLY = /^\s*(?:\/\*[\s\S]*?\*\/\s*)*$/;
|
|
5
6
|
export const remarkMdxExtractPanel = (mdxExtracts) => {
|
|
6
7
|
return (tree) => {
|
|
7
8
|
let esmContent = '';
|
|
8
9
|
let panelContent;
|
|
10
|
+
const sanitizeNode = (node) => {
|
|
11
|
+
const sanitized = Object.assign({}, node);
|
|
12
|
+
if ('attributes' in sanitized && Array.isArray(sanitized.attributes)) {
|
|
13
|
+
sanitized.attributes = sanitized.attributes.map((attr) => {
|
|
14
|
+
if (attr.type === 'mdxJsxAttribute' && typeof attr.value === 'number') {
|
|
15
|
+
const value = {
|
|
16
|
+
type: 'mdxJsxAttributeValueExpression',
|
|
17
|
+
value: String(attr.value),
|
|
18
|
+
};
|
|
19
|
+
return Object.assign(Object.assign({}, attr), { value });
|
|
20
|
+
}
|
|
21
|
+
return attr;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if ('children' in sanitized && Array.isArray(sanitized.children)) {
|
|
25
|
+
sanitized.children = sanitized.children.map(sanitizeNode);
|
|
26
|
+
}
|
|
27
|
+
return sanitized;
|
|
28
|
+
};
|
|
9
29
|
const stringifyNode = (node) => {
|
|
10
30
|
try {
|
|
11
|
-
return unified()
|
|
31
|
+
return unified()
|
|
32
|
+
.use(coreRemarkMdxPlugins)
|
|
33
|
+
.use(remarkStringify)
|
|
34
|
+
.stringify(sanitizeNode(node));
|
|
12
35
|
}
|
|
13
36
|
catch (error) {
|
|
14
37
|
console.error('Error converting MDX content to markdown:', error);
|
|
15
|
-
return
|
|
38
|
+
return null;
|
|
16
39
|
}
|
|
17
40
|
};
|
|
41
|
+
const isBlankNode = (node) => {
|
|
42
|
+
if (node.type === 'text')
|
|
43
|
+
return node.value.trim() === '';
|
|
44
|
+
if (node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') {
|
|
45
|
+
return COMMENT_ONLY.test(node.value);
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
};
|
|
49
|
+
const isEmptyExtract = (children) => children.every(isBlankNode);
|
|
50
|
+
const isPanelElement = (node) => node.type === 'mdxJsxFlowElement' && node.name === 'Panel';
|
|
51
|
+
// The extracted content is recompiled through this same pipeline, so any
|
|
52
|
+
// nested <Panel> markup left in it would be stripped on the second pass and
|
|
53
|
+
// its body lost. Flatten every nested Panel into its own children (and warn)
|
|
54
|
+
// so the extract carries no Panel markup and the content renders inline.
|
|
55
|
+
let nestedPanelFlattened = false;
|
|
56
|
+
const unwrapNestedPanels = (children) => children.flatMap((child) => {
|
|
57
|
+
if (isPanelElement(child)) {
|
|
58
|
+
nestedPanelFlattened = true;
|
|
59
|
+
return unwrapNestedPanels(child.children);
|
|
60
|
+
}
|
|
61
|
+
if ('children' in child && Array.isArray(child.children)) {
|
|
62
|
+
const container = child;
|
|
63
|
+
return [
|
|
64
|
+
Object.assign(Object.assign({}, container), { children: unwrapNestedPanels(container.children) }),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [child];
|
|
68
|
+
});
|
|
18
69
|
visit(tree, 'mdxjsEsm', (node) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
70
|
+
const stringified = stringifyNode({ type: 'root', children: [node] });
|
|
71
|
+
if (stringified)
|
|
72
|
+
esmContent += stringified;
|
|
23
73
|
});
|
|
74
|
+
let canonicalHandled = false;
|
|
24
75
|
visit(tree, 'mdxJsxFlowElement', (node, i, parent) => {
|
|
25
|
-
if (node.name
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
children: node.children,
|
|
29
|
-
});
|
|
76
|
+
if (node.name !== 'Panel')
|
|
77
|
+
return;
|
|
78
|
+
const removeSelf = () => {
|
|
30
79
|
if (parent && i != null) {
|
|
31
80
|
parent.children.splice(i, 1);
|
|
81
|
+
return [SKIP, i];
|
|
32
82
|
}
|
|
83
|
+
return SKIP;
|
|
84
|
+
};
|
|
85
|
+
if (canonicalHandled) {
|
|
86
|
+
console.warn('Only one <Panel> is supported per page — the extra <Panel> was removed. Move its content into the single panel.');
|
|
87
|
+
return removeSelf();
|
|
88
|
+
}
|
|
89
|
+
// An empty or comment-only Panel contributes no side panel, so it must not
|
|
90
|
+
// claim the single canonical slot — remove it and let a later non-empty
|
|
91
|
+
// Panel become the page's panel.
|
|
92
|
+
if (isEmptyExtract(node.children)) {
|
|
93
|
+
return removeSelf();
|
|
94
|
+
}
|
|
95
|
+
const content = stringifyNode({ type: 'root', children: unwrapNestedPanels(node.children) });
|
|
96
|
+
if (content == null) {
|
|
97
|
+
return SKIP;
|
|
98
|
+
}
|
|
99
|
+
if (nestedPanelFlattened) {
|
|
100
|
+
console.warn('A nested <Panel> was flattened into the page panel — <Panel> components cannot be nested.');
|
|
33
101
|
}
|
|
102
|
+
canonicalHandled = true;
|
|
103
|
+
panelContent = content;
|
|
104
|
+
return removeSelf();
|
|
34
105
|
});
|
|
35
106
|
if (esmContent || panelContent) {
|
|
36
107
|
const content = [esmContent, panelContent].filter((content) => !!content).join('\n');
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { visit } from 'unist-util-visit';
|
|
2
|
+
import { isMdxJsxFlowElement } from '../../utils.js';
|
|
3
|
+
const countTabs = (children) => children.reduce((count, child) => {
|
|
4
|
+
if (!isMdxJsxFlowElement(child))
|
|
5
|
+
return count;
|
|
6
|
+
if (child.name == null)
|
|
7
|
+
return count + countTabs(child.children);
|
|
8
|
+
return child.name === 'Tab' ? count + 1 : count;
|
|
9
|
+
}, 0);
|
|
2
10
|
export const remarkSplitTabs = () => (tree) => {
|
|
3
11
|
const tabsToProcess = [];
|
|
4
12
|
visit(tree, 'mdxJsxFlowElement', (node, index, parent) => {
|
|
@@ -9,7 +17,7 @@ export const remarkSplitTabs = () => (tree) => {
|
|
|
9
17
|
// process in reverse document order so splices don't shift the saved
|
|
10
18
|
// indices of earlier siblings or clone-before-split nested Tabs
|
|
11
19
|
for (const { node, index, parent } of tabsToProcess.reverse()) {
|
|
12
|
-
const numberOfTabs = node.children
|
|
20
|
+
const numberOfTabs = countTabs(node.children);
|
|
13
21
|
if (numberOfTabs <= 1)
|
|
14
22
|
continue;
|
|
15
23
|
const newNodes = [];
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { visitParents } from 'unist-util-visit-parents';
|
|
2
|
+
import { isMdxJsxFlowElement, isMdxJsxFragment } from '../../utils.js';
|
|
2
3
|
export const remarkValidateTabs = () => (tree) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
visitParents(tree, 'mdxJsxFlowElement', (node, ancestors) => {
|
|
5
|
+
var _a;
|
|
6
|
+
if (node.name !== 'Tab')
|
|
7
|
+
return;
|
|
8
|
+
const meaningfulAncestors = ancestors.filter((ancestor) => !isMdxJsxFragment(ancestor));
|
|
9
|
+
const parent = meaningfulAncestors[meaningfulAncestors.length - 1];
|
|
10
|
+
const parentIsTabs = parent !== undefined && isMdxJsxFlowElement(parent) && parent.name === 'Tabs';
|
|
11
|
+
if (!parentIsTabs) {
|
|
12
|
+
console.warn('Please ensure that all <Tab> components are direct children of <Tabs>.');
|
|
13
|
+
}
|
|
14
|
+
const title = (_a = node.attributes.find((attr) => 'name' in attr && attr.name === 'title')) === null || _a === void 0 ? void 0 : _a.value;
|
|
15
|
+
if (title === '') {
|
|
16
|
+
console.warn('<Tab> has an empty title — the tab has no accessible name and cannot deep-link. Add a descriptive title.');
|
|
9
17
|
}
|
|
10
18
|
});
|
|
11
19
|
};
|
package/dist/rss/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import slugify from '@sindresorhus/slugify';
|
|
2
2
|
import { stringifyTree } from '../mdx/index.js';
|
|
3
3
|
import { getArrayExpressionStringProperties, getObjectExpressionStringProperty, } from '../mdx/utils.js';
|
|
4
|
+
import { safeCleanHeadingId } from '../slugify.js';
|
|
4
5
|
export const isFrontmatter = (node) => {
|
|
5
6
|
return (node === null || node === void 0 ? void 0 : node.type) === 'yaml';
|
|
6
7
|
};
|
|
@@ -85,6 +86,17 @@ export const getRssPropsData = (updateComponent) => {
|
|
|
85
86
|
const description = getObjectExpressionStringProperty('description', rssData);
|
|
86
87
|
return { rssTitle: title, rssDescription: description };
|
|
87
88
|
};
|
|
89
|
+
const getUpdateAnchorId = (updateComponent) => {
|
|
90
|
+
const idAttribute = updateComponent.attributes.find((attribute) => attribute.type === 'mdxJsxAttribute' &&
|
|
91
|
+
attribute.name === 'id' &&
|
|
92
|
+
typeof attribute.value === 'string');
|
|
93
|
+
if (typeof (idAttribute === null || idAttribute === void 0 ? void 0 : idAttribute.value) !== 'string')
|
|
94
|
+
return undefined;
|
|
95
|
+
// Match the page's id pipeline (Update.tsx uses safeCleanHeadingId) so feed
|
|
96
|
+
// fragment links point at the same DOM id the page renders; the RSS pipeline
|
|
97
|
+
// never runs the ToC plugin, so the cleaning must happen here.
|
|
98
|
+
return safeCleanHeadingId(idAttribute.value) || undefined;
|
|
99
|
+
};
|
|
88
100
|
export const getUpdateTitle = (updateComponent) => {
|
|
89
101
|
var _a;
|
|
90
102
|
const attributes = updateComponent.attributes;
|
|
@@ -313,7 +325,7 @@ export const processUpdatePerNode = ({ updateNode, date, title, description, })
|
|
|
313
325
|
return [];
|
|
314
326
|
}
|
|
315
327
|
const rssTitle = title || label;
|
|
316
|
-
const anchor = slugify(label);
|
|
328
|
+
const anchor = getUpdateAnchorId(updateNode) || slugify(label);
|
|
317
329
|
const categories = getTags(updateNode);
|
|
318
330
|
const contentNodes = updateNode.children.filter((child) => isNormalMarkdown(child));
|
|
319
331
|
const contentString = stringifyTreeForRSS({
|