@mintlify/common 1.0.1033 → 1.0.1035

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.
@@ -18,6 +18,7 @@ export * from './remarkValidateAccordions.js';
18
18
  export * from './remarkValidateSteps.js';
19
19
  export * from './remarkValidateVisibility.js';
20
20
  export * from './remarkValidateTabs.js';
21
+ export * from './remarkValidateApiExamples.js';
21
22
  export * from './remarkVideo.js';
22
23
  export * from './remarkExtractMultiView.js';
23
24
  export * from './remarkPrompt.js';
@@ -18,6 +18,7 @@ export * from './remarkValidateAccordions.js';
18
18
  export * from './remarkValidateSteps.js';
19
19
  export * from './remarkValidateVisibility.js';
20
20
  export * from './remarkValidateTabs.js';
21
+ export * from './remarkValidateApiExamples.js';
21
22
  export * from './remarkVideo.js';
22
23
  export * from './remarkExtractMultiView.js';
23
24
  export * from './remarkPrompt.js';
@@ -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().use(coreRemarkMdxPlugins).use(remarkStringify).stringify(node);
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
- esmContent += stringifyNode({
20
- type: 'root',
21
- children: [node],
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 === 'Panel') {
26
- panelContent = stringifyNode({
27
- type: 'root',
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');
@@ -0,0 +1,2 @@
1
+ import type { Root } from 'mdast';
2
+ export declare const remarkValidateApiExamples: () => (tree: Root) => void;
@@ -0,0 +1,15 @@
1
+ import { visit } from 'unist-util-visit';
2
+ export const remarkValidateApiExamples = () => (tree) => {
3
+ const counts = { RequestExample: 0, ResponseExample: 0 };
4
+ visit(tree, 'mdxJsxFlowElement', (node) => {
5
+ if (node.name === 'RequestExample' || node.name === 'ResponseExample') {
6
+ counts[node.name] += 1;
7
+ }
8
+ });
9
+ if (counts.RequestExample > 1) {
10
+ console.warn('Only one <RequestExample> is supported per page.');
11
+ }
12
+ if (counts.ResponseExample > 1) {
13
+ console.warn('Only one <ResponseExample> is supported per page.');
14
+ }
15
+ };
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { serialize } from '@mintlify/mdx/server';
11
11
  import { getTailwindSelectors } from '../../css/tailwind.js';
12
- import { getMDXOptions, remarkMdxRemoveJs, remarkExpandContent, remarkSplitCodeGroup, remarkSplitTabs, remarkValidateAccordions, remarkValidateSteps, remarkValidateVisibility, remarkValidateTabs, } from '../../index.js';
12
+ import { getMDXOptions, remarkMdxRemoveJs, remarkExpandContent, remarkSplitCodeGroup, remarkSplitTabs, remarkValidateAccordions, remarkValidateSteps, remarkValidateVisibility, remarkValidateTabs, remarkValidateApiExamples, } from '../../index.js';
13
13
  import { codeStylingToThemeOrThemes } from '../getCodeStyling.js';
14
14
  import { preprocessCustomHeadingIds } from '../preprocessCustomHeadingIds.js';
15
15
  import { replaceVariables } from '../replaceVariables.js';
@@ -37,6 +37,7 @@ export function getMdx(_a) {
37
37
  remarkValidateTabs,
38
38
  remarkValidateVisibility,
39
39
  remarkValidateAccordions,
40
+ remarkValidateApiExamples,
40
41
  ...remarkPlugins,
41
42
  ];
42
43
  if (pageType === 'pdf') {