@mintlify/common 1.0.806 → 1.0.808

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.
@@ -0,0 +1,2 @@
1
+ import { AnyRecord } from '../types/asyncapi.js';
2
+ export declare const getEnumValues: (value: AnyRecord | undefined) => Array<string | number | boolean> | undefined;
@@ -0,0 +1,7 @@
1
+ export const getEnumValues = (value) => {
2
+ if (!Array.isArray(value === null || value === void 0 ? void 0 : value.enum)) {
3
+ return undefined;
4
+ }
5
+ const enumValues = value.enum.filter((option) => typeof option === 'string' || typeof option === 'number' || typeof option === 'boolean');
6
+ return enumValues.length > 0 ? enumValues : undefined;
7
+ };
@@ -2,4 +2,5 @@ export * from './getAsyncApiDocumentFromUrl.js';
2
2
  export * from './validateAsyncApi.js';
3
3
  export * from './parseAsyncApiString.js';
4
4
  export * from './parser/getChannelData.js';
5
+ export * from './getEnumValues.js';
5
6
  export * from './prepAsyncApiFrontmatter.js';
@@ -2,4 +2,5 @@ export * from './getAsyncApiDocumentFromUrl.js';
2
2
  export * from './validateAsyncApi.js';
3
3
  export * from './parseAsyncApiString.js';
4
4
  export * from './parser/getChannelData.js';
5
+ export * from './getEnumValues.js';
5
6
  export * from './prepAsyncApiFrontmatter.js';
@@ -1,3 +1,23 @@
1
+ import { getEnumValues } from '../getEnumValues.js';
2
+ const getSchemaType = (value, keys) => {
3
+ if ('type' in value) {
4
+ return value.type;
5
+ }
6
+ const customTypeKey = keys.find((key) => /^x-.*type$/.test(key));
7
+ if (customTypeKey) {
8
+ return value[customTypeKey];
9
+ }
10
+ const enumValues = getEnumValues(value);
11
+ if (enumValues === null || enumValues === void 0 ? void 0 : enumValues.length) {
12
+ const firstValue = enumValues[0];
13
+ if (typeof firstValue === 'boolean')
14
+ return 'boolean';
15
+ if (typeof firstValue === 'number')
16
+ return 'number';
17
+ return 'string';
18
+ }
19
+ return undefined;
20
+ };
1
21
  export const extractSchemaProperties = (schema) => {
2
22
  if (!schema) {
3
23
  return [];
@@ -36,12 +56,13 @@ export const extractSchemaProperties = (schema) => {
36
56
  }
37
57
  if (typeof value === 'object' && !Array.isArray(value)) {
38
58
  const keys = Object.keys(value);
39
- if ('type' in value || keys.some((key) => /^x-.*type$/.test(key))) {
40
- const typeKey = 'type' in value ? 'type' : keys.find((key) => /^x-.*type$/.test(key));
59
+ const type = getSchemaType(value, keys);
60
+ if (type !== undefined) {
41
61
  const prop = {
42
62
  name: name,
43
- type: value[typeKey],
63
+ type,
44
64
  description: value.description || value['const'],
65
+ enumValues: getEnumValues(value),
45
66
  deprecated: value.deprecated,
46
67
  required: requiredFields.has(name),
47
68
  };
@@ -3,6 +3,7 @@ export * from './snippets/index.js';
3
3
  export * from './utils.js';
4
4
  export * from './plugins/index.js';
5
5
  export * from './lib/index.js';
6
+ export * from './preprocessCustomHeadingIds.js';
6
7
  export * from './getMDXOptions.js';
7
8
  export * from './astUtils.js';
8
9
  export * from './getS3ImageUri.js';
package/dist/mdx/index.js CHANGED
@@ -3,6 +3,7 @@ export * from './snippets/index.js';
3
3
  export * from './utils.js';
4
4
  export * from './plugins/index.js';
5
5
  export * from './lib/index.js';
6
+ export * from './preprocessCustomHeadingIds.js';
6
7
  export * from './getMDXOptions.js';
7
8
  export * from './astUtils.js';
8
9
  export * from './getS3ImageUri.js';
@@ -37,7 +37,7 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
37
37
  const tabSlugs = new Map();
38
38
  const headingSlugs = new Map();
39
39
  const precomputeTabSlugs = (node) => {
40
- var _a, _b;
40
+ var _a, _b, _c;
41
41
  if (node.type === 'mdxJsxFlowElement' && node.name === 'Tab') {
42
42
  const title = (_a = node.attributes.find((attr) => 'name' in attr && attr.name === 'title')) === null || _a === void 0 ? void 0 : _a.value;
43
43
  if (title && typeof title === 'string') {
@@ -45,8 +45,9 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
45
45
  tabSlugs.set(node, slug);
46
46
  }
47
47
  }
48
- else if (node.type === 'mdxJsxFlowElement' && node.name === 'Heading') {
49
- const id = (_b = node.attributes.find((attr) => 'name' in attr && attr.name === 'id')) === null || _b === void 0 ? void 0 : _b.value;
48
+ else if (node.type === 'mdxJsxFlowElement' &&
49
+ (node.name === 'Heading' || ['h1', 'h2', 'h3', 'h4'].includes((_b = node.name) !== null && _b !== void 0 ? _b : ''))) {
50
+ const id = (_c = node.attributes.find((attr) => 'name' in attr && attr.name === 'id')) === null || _c === void 0 ? void 0 : _c.value;
50
51
  if (id && typeof id === 'string')
51
52
  headingSlugs.set(node, id);
52
53
  }
@@ -90,9 +91,16 @@ export const remarkComponentIds = (pageMetadata) => (tree) => {
90
91
  }
91
92
  }
92
93
  };
94
+ const isHeadingNode = (node) => {
95
+ var _a;
96
+ return node.type === 'mdxJsxFlowElement' &&
97
+ (node.name === 'Heading' || ['h1', 'h2', 'h3', 'h4'].includes((_a = node.name) !== null && _a !== void 0 ? _a : ''));
98
+ };
93
99
  const collectDirectChildIds = (node, type) => {
94
100
  const childIds = [];
95
- if (node.type === 'mdxJsxFlowElement' && node.name === type) {
101
+ const isMatch = node.type === 'mdxJsxFlowElement' &&
102
+ (type === 'Tab' ? node.name === 'Tab' : isHeadingNode(node));
103
+ if (isMatch) {
96
104
  const id = type === 'Tab' ? tabSlugs.get(node) : headingSlugs.get(node);
97
105
  if (id) {
98
106
  childIds.push(id);
@@ -1,4 +1,3 @@
1
- import { slugifyWithCounter } from '@sindresorhus/slugify';
2
1
  import { visit } from 'unist-util-visit';
3
2
  import { slugify } from '../../../slugify.js';
4
3
  import { createMdxJsxAttribute, getTableOfContentsTitle } from '../../lib/remark-utils.js';
@@ -19,9 +18,20 @@ function getStringAttribute(attributes, name) {
19
18
  return undefined;
20
19
  }
21
20
  export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
22
- // slugifyWithCounter adds a counter (eg. slug, slug-2, slug-3) to the end of the slug if the header
23
- // already exists. No counter is added for the first occurence.
24
- const slugifyFn = slugifyWithCounter();
21
+ // Unified slug deduplication: both custom IDs and auto-generated slugs
22
+ // share the same seen-slug state so they cannot collide.
23
+ const seenSlugs = new Map();
24
+ const deduplicateSlug = (slug) => {
25
+ var _a;
26
+ const count = (_a = seenSlugs.get(slug)) !== null && _a !== void 0 ? _a : 0;
27
+ seenSlugs.set(slug, count + 1);
28
+ if (count === 0)
29
+ return slug;
30
+ const suffixed = `${slug}-${count + 1}`;
31
+ if (!seenSlugs.has(suffixed))
32
+ seenSlugs.set(suffixed, 0);
33
+ return suffixed;
34
+ };
25
35
  return (tree) => {
26
36
  const contents = [];
27
37
  let hasTopLayer = false;
@@ -58,7 +68,7 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
58
68
  }
59
69
  });
60
70
  visit(tree, (node) => {
61
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
71
+ var _a, _b, _c, _d, _e, _f, _g, _h;
62
72
  if (excludedNodes.has(node))
63
73
  return;
64
74
  const currentTabId = tabContentMap.get(node);
@@ -110,6 +120,10 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
110
120
  else if ('name' in node && ((_g = node.name) === null || _g === void 0 ? void 0 : _g[1])) {
111
121
  const num = Number(node.name[1]);
112
122
  level = !isNaN(num) ? num : undefined;
123
+ if (level !== undefined) {
124
+ // @ts-expect-error we're assigning to depth despite the node not containing depth in the type
125
+ node.depth = level;
126
+ }
113
127
  }
114
128
  let title;
115
129
  if ('name' in node && node.name === 'Step') {
@@ -119,28 +133,46 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
119
133
  title = getTableOfContentsTitle(node);
120
134
  }
121
135
  let slug;
122
- if ('name' in node &&
123
- (node.name === 'Heading' || node.name === 'Step' || node.name === 'Update')) {
124
- slug = (_j = getStringAttribute(node.attributes, 'id')) !== null && _j !== void 0 ? _j : slugify(title, slugifyFn);
136
+ const explicitId = ('name' in node &&
137
+ (node.name === 'Heading' || node.name === 'Step' || node.name === 'Update')) ||
138
+ hasIdAttribute
139
+ ? getStringAttribute(node.attributes, 'id')
140
+ : undefined;
141
+ if (explicitId) {
142
+ slug = deduplicateSlug(explicitId);
125
143
  }
126
144
  else {
127
- slug = slugify(title, slugifyFn);
145
+ slug = deduplicateSlug(slugify(title));
128
146
  }
129
147
  let mdxJsxAttributes;
130
148
  if ('name' in node && node.name === 'Update') {
131
149
  const existingId = getStringAttribute(node.attributes, 'id');
132
- mdxJsxAttributes = existingId
133
- ? node.attributes
134
- : [...node.attributes, createMdxJsxAttribute('id', slug)];
150
+ mdxJsxAttributes = !existingId
151
+ ? [...node.attributes, createMdxJsxAttribute('id', slug)]
152
+ : existingId !== slug
153
+ ? node.attributes.map((attr) => 'name' in attr && attr.name === 'id' ? createMdxJsxAttribute('id', slug) : attr)
154
+ : node.attributes;
135
155
  }
136
156
  else if ('name' in node && node.name === 'Step') {
137
157
  const existingId = getStringAttribute(node.attributes, 'id');
138
- mdxJsxAttributes = existingId
139
- ? node.attributes
140
- : [...node.attributes, createMdxJsxAttribute('id', slug)];
158
+ mdxJsxAttributes = !existingId
159
+ ? [...node.attributes, createMdxJsxAttribute('id', slug)]
160
+ : existingId !== slug
161
+ ? node.attributes.map((attr) => 'name' in attr && attr.name === 'id' ? createMdxJsxAttribute('id', slug) : attr)
162
+ : node.attributes;
141
163
  }
142
164
  else if ('name' in node && node.name === 'Heading') {
143
- mdxJsxAttributes = node.attributes;
165
+ mdxJsxAttributes =
166
+ explicitId && slug !== explicitId
167
+ ? node.attributes.map((attr) => 'name' in attr && attr.name === 'id' ? createMdxJsxAttribute('id', slug) : attr)
168
+ : node.attributes;
169
+ }
170
+ else if (level !== undefined && hasIdAttribute) {
171
+ // JSX heading with an explicit id attribute (e.g. <h2 id="custom-id">)
172
+ const updatedAttrs = explicitId && slug !== explicitId
173
+ ? node.attributes.map((attr) => 'name' in attr && attr.name === 'id' ? createMdxJsxAttribute('id', slug) : attr)
174
+ : node.attributes;
175
+ mdxJsxAttributes = [createMdxJsxAttribute('level', level), ...updatedAttrs];
144
176
  }
145
177
  else if (level !== undefined && !hasIdAttribute) {
146
178
  mdxJsxAttributes = [
@@ -151,6 +183,14 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
151
183
  mdxJsxAttributes.push(...node.attributes);
152
184
  }
153
185
  }
186
+ // Unwrap paragraph children inside JSX headings (e.g. <h2>) so that the
187
+ // Heading component receives inline children instead of <p>-wrapped text.
188
+ // This happens because the preprocessor places text on its own line between
189
+ // JSX tags, which MDX parses as a paragraph block.
190
+ if (isValidMdxHeading && 'children' in node) {
191
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
+ node.children = node.children.flatMap((child) => child.type === 'paragraph' && 'children' in child ? child.children : [child]);
193
+ }
154
194
  // @ts-expect-error we're assigning over 'attributes' if it doesn't exist
155
195
  node.attributes = mdxJsxAttributes;
156
196
  node.type = 'mdxJsxFlowElement';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Converts markdown headings with custom ID syntax into JSX heading elements.
3
+ *
4
+ * Transforms `## Heading text {#custom-id}` into `<h2 id="custom-id">Heading text</h2>`.
5
+ * This avoids MDX parsing errors (MDX treats `{...}` as JS expressions) and
6
+ * produces JSX heading elements that the existing remark pipeline already handles.
7
+ */
8
+ export declare function preprocessCustomHeadingIds(content: string): string;
@@ -0,0 +1,20 @@
1
+ import slugify from '@sindresorhus/slugify';
2
+ /**
3
+ * Converts markdown headings with custom ID syntax into JSX heading elements.
4
+ *
5
+ * Transforms `## Heading text {#custom-id}` into `<h2 id="custom-id">Heading text</h2>`.
6
+ * This avoids MDX parsing errors (MDX treats `{...}` as JS expressions) and
7
+ * produces JSX heading elements that the existing remark pipeline already handles.
8
+ */
9
+ export function preprocessCustomHeadingIds(content) {
10
+ // Single pass: match code fences (returned unchanged) or headings with
11
+ // custom IDs (transformed to JSX). Backreferences \1 / \2 ensure the
12
+ // closing fence matches the opening fence length.
13
+ return content.replace(/^(`{3,})[^\n]*\n[\s\S]*?^\1\s*$|^(~{3,})[^\n]*\n[\s\S]*?^\2\s*$|^(#{1,4})\s+(.+?)\s*\{\s*#([^}]+?)\s*\}\s*$/gm, (match, _backtickFence, _tildeFence, hashes, text, id) => {
14
+ if (!hashes)
15
+ return match; // code fence — leave unchanged
16
+ const level = hashes.length;
17
+ const sanitizedId = slugify(id.trim(), { preserveCharacters: ['_'] });
18
+ return `<h${level} id="${sanitizedId}">\n${text}\n</h${level}>`;
19
+ });
20
+ }
@@ -4,6 +4,7 @@ import remarkGfm from 'remark-gfm';
4
4
  import remarkMath from 'remark-math';
5
5
  import remarkMdx from 'remark-mdx';
6
6
  import remarkStringify from 'remark-stringify';
7
+ import { preprocessCustomHeadingIds } from './preprocessCustomHeadingIds.js';
7
8
  import { getJsxEsmTree } from './snippets/getJsxEsmTree.js';
8
9
  import { isJsxOrTsx } from './snippets/isJsxOrTsx.js';
9
10
  export const coreRemarkMdxPlugins = [
@@ -17,6 +18,6 @@ export const getAST = (str, filePath) => {
17
18
  if (isJsxOrTsx(filePath)) {
18
19
  return getJsxEsmTree(str, filePath);
19
20
  }
20
- return coreRemark().parse(str);
21
+ return coreRemark().parse(preprocessCustomHeadingIds(str));
21
22
  };
22
23
  export const stringifyTree = (tree) => coreRemark().use(remarkStringify).stringify(tree);
@@ -11,13 +11,14 @@ import { serialize } from '@mintlify/mdx/server';
11
11
  import { getTailwindSelectors } from '../../css/tailwind.js';
12
12
  import { getMDXOptions, remarkMdxRemoveJs, remarkExpandContent, remarkSplitCodeGroup, remarkSplitTabs, remarkValidateSteps, remarkValidateTabs, } from '../../index.js';
13
13
  import { codeStylingToThemeOrThemes } from '../getCodeStyling.js';
14
+ import { preprocessCustomHeadingIds } from '../preprocessCustomHeadingIds.js';
14
15
  import { replaceVariables } from '../replaceVariables.js';
15
16
  import { createSnippetTreeMap } from './getMdx/snippets.js';
16
17
  export function getMdx(_a) {
17
18
  return __awaiter(this, arguments, void 0, function* ({ path, content, metadata, snippets, subdomain, codeStyling, config, tailwindSelectors = undefined, pageType = 'default', trace = undefined, customLanguages = [], variables = undefined, }) {
18
19
  const traceFn = trace !== null && trace !== void 0 ? trace : ((_name, fn) => fn());
19
- const processedContent = replaceVariables(content, variables);
20
- const processedSnippets = snippets.map((snippet) => (Object.assign(Object.assign({}, snippet), { content: replaceVariables(snippet.content, variables) })));
20
+ const processedContent = preprocessCustomHeadingIds(replaceVariables(content, variables));
21
+ const processedSnippets = snippets.map((snippet) => (Object.assign(Object.assign({}, snippet), { content: preprocessCustomHeadingIds(replaceVariables(snippet.content, variables)) })));
21
22
  if (!tailwindSelectors)
22
23
  tailwindSelectors = yield traceFn('getMdx.getTailwindSelectors', () => __awaiter(this, void 0, void 0, function* () { return getTailwindSelectors({ content: processedContent }); }));
23
24
  const snippetTreeMap = yield traceFn('getMdx.createSnippetTreeMap', () => __awaiter(this, void 0, void 0, function* () { return createSnippetTreeMap(processedSnippets); }));