@mintlify/common 1.0.816 → 1.0.818

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.
@@ -1,4 +1,4 @@
1
- import { rehypeCodeBlocks, rehypeDynamicTailwindCss, rehypeKeyboardSymbols, rehypeListItemText, rehypeMdxExtractEndpoint, rehypeMdxExtractExamples, rehypeParamFieldIds, rehypeRawComponents, rehypeTable, rehypeUnicodeIds, rehypeZoomImages, remarkExtractChangelogFilters, remarkExtractTableOfContents, remarkFrames, remarkComponentIds, remarkMdxInjectSnippets, remarkMdxRemoveUnusedVariables, remarkRemoveImports, remarkMdxExtractPanel, remarkVideo, remarkExtractMultiView, remarkPrompt, } from './plugins/index.js';
1
+ import { rehypeCodeBlocks, rehypeDynamicTailwindCss, rehypeKeyboardSymbols, rehypeListItemText, rehypeMdxExtractEndpoint, rehypeMdxExtractExamples, rehypeParamFieldIds, rehypeRawComponents, rehypeTable, rehypeUnicodeIds, rehypeZoomImages, remarkExtractChangelogFilters, remarkExtractTableOfContents, remarkFrames, remarkComponentIds, remarkMdxInjectSnippets, remarkMdxRemoveUnusedVariables, remarkRemoveImports, remarkMdxExtractPanel, remarkVideo, remarkExtractMultiView, remarkPrompt, remarkUnwrapJsxHeadings, } from './plugins/index.js';
2
2
  import { remarkMdxRemoveUnknownJsx } from './plugins/remark/remarkMdxRemoveUnknownJsx/index.js';
3
3
  import { remarkMermaid } from './plugins/remark/remarkMermaid.js';
4
4
  // avoid running extractors unnecessarily
@@ -16,6 +16,7 @@ export const getMDXOptions = ({ data, remarkPlugins = [], rehypePlugins = [], md
16
16
  [remarkMdxInjectSnippets, data.snippetTreeMap],
17
17
  remarkPrompt,
18
18
  [remarkComponentIds, data.pageMetadata],
19
+ remarkUnwrapJsxHeadings,
19
20
  [remarkExtractTableOfContents, mdxExtracts, data.pageMetadata], // modifies tree so cannot be excluded
20
21
  [remarkExtractChangelogFilters, mdxExtracts],
21
22
  [remarkMdxExtractPanel, mdxExtracts],
@@ -17,3 +17,4 @@ export * from './remarkValidateTabs.js';
17
17
  export * from './remarkVideo.js';
18
18
  export * from './remarkExtractMultiView.js';
19
19
  export * from './remarkPrompt.js';
20
+ export * from './remarkUnwrapJsxHeadings.js';
@@ -17,3 +17,4 @@ export * from './remarkValidateTabs.js';
17
17
  export * from './remarkVideo.js';
18
18
  export * from './remarkExtractMultiView.js';
19
19
  export * from './remarkPrompt.js';
20
+ export * from './remarkUnwrapJsxHeadings.js';
@@ -183,14 +183,6 @@ export const remarkExtractTableOfContents = (mdxExtracts, pageMetadata) => {
183
183
  mdxJsxAttributes.push(...node.attributes);
184
184
  }
185
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
- }
194
186
  // @ts-expect-error we're assigning over 'attributes' if it doesn't exist
195
187
  node.attributes = mdxJsxAttributes;
196
188
  node.type = 'mdxJsxFlowElement';
@@ -0,0 +1,9 @@
1
+ import type { Root } from 'mdast';
2
+ /**
3
+ * Unwraps paragraph children inside JSX heading elements (h1–h6).
4
+ *
5
+ * The custom-ID preprocessor emits headings like `<h2 id="x">\nText\n</h2>`,
6
+ * and MDX parses the text on its own line as a paragraph block. This plugin
7
+ * flattens those paragraphs so the heading receives inline children directly.
8
+ */
9
+ export declare const remarkUnwrapJsxHeadings: () => (tree: Root) => void;
@@ -0,0 +1,18 @@
1
+ import { visit } from 'unist-util-visit';
2
+ import { ALL_HEADING_NAMES } from '../../preprocessCustomHeadingIds.js';
3
+ /**
4
+ * Unwraps paragraph children inside JSX heading elements (h1–h6).
5
+ *
6
+ * The custom-ID preprocessor emits headings like `<h2 id="x">\nText\n</h2>`,
7
+ * and MDX parses the text on its own line as a paragraph block. This plugin
8
+ * flattens those paragraphs so the heading receives inline children directly.
9
+ */
10
+ export const remarkUnwrapJsxHeadings = () => (tree) => {
11
+ visit(tree, 'mdxJsxFlowElement', (node) => {
12
+ var _a;
13
+ if (!ALL_HEADING_NAMES.includes((_a = node.name) !== null && _a !== void 0 ? _a : ''))
14
+ return;
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ node.children = node.children.flatMap((child) => child.type === 'paragraph' && 'children' in child ? child.children : [child]);
17
+ });
18
+ };
@@ -6,9 +6,13 @@ import { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';
6
6
  * Transforms `## Heading text {#custom-id}` into `<h2 id="custom-id">Heading text</h2>`.
7
7
  * This avoids MDX parsing errors (MDX treats `{...}` as JS expressions) and
8
8
  * produces JSX heading elements that the existing remark pipeline already handles.
9
+ *
10
+ * Uses a line-by-line scan with fence state tracking to skip headings inside
11
+ * code blocks, avoiding backtracking over large documents.
9
12
  */
10
13
  export declare function preprocessCustomHeadingIds(content: string): string;
11
14
  export interface JsxHeadingElement extends MdxJsxFlowElement {
12
15
  readonly __jsxHeading: true;
13
16
  }
17
+ export declare const ALL_HEADING_NAMES: string[];
14
18
  export declare const isJsxHeadingElement: (node: RootContent) => node is JsxHeadingElement;
@@ -1,27 +1,74 @@
1
1
  import slugify from '@sindresorhus/slugify';
2
- import { HEADING_NAMES } from './plugins/remark/remarkExtractTableOfContents.js';
2
+ const HEADING_ID_RE = /^(#{1,6})\s+(.+?)\s*\{\s*#([^}]+?)\s*\}\s*$/;
3
3
  /**
4
4
  * Converts markdown headings with custom ID syntax into JSX heading elements.
5
5
  *
6
6
  * Transforms `## Heading text {#custom-id}` into `<h2 id="custom-id">Heading text</h2>`.
7
7
  * This avoids MDX parsing errors (MDX treats `{...}` as JS expressions) and
8
8
  * produces JSX heading elements that the existing remark pipeline already handles.
9
+ *
10
+ * Uses a line-by-line scan with fence state tracking to skip headings inside
11
+ * code blocks, avoiding backtracking over large documents.
9
12
  */
10
13
  export function preprocessCustomHeadingIds(content) {
11
- // Single pass: match code fences (returned unchanged) or headings with
12
- // custom IDs (transformed to JSX). Backreferences \1 / \2 ensure the
13
- // closing fence matches the opening fence length.
14
- 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) => {
15
- if (!hashes)
16
- return match; // code fence — leave unchanged
17
- const level = hashes.length;
18
- const sanitizedId = slugify(id.trim(), { preserveCharacters: ['_'] });
19
- return `<h${level} id="${sanitizedId}">\n${text}\n</h${level}>`;
20
- });
14
+ const lines = content.split('\n');
15
+ const result = [];
16
+ let fenceChar;
17
+ let fenceCount = 0;
18
+ for (const line of lines) {
19
+ const stripped = line.trimStart();
20
+ if (fenceChar !== undefined) {
21
+ if (isClosingFence(stripped, fenceChar, fenceCount)) {
22
+ fenceChar = undefined;
23
+ }
24
+ result.push(line);
25
+ continue;
26
+ }
27
+ const fence = parseOpeningFence(stripped);
28
+ if (fence) {
29
+ fenceChar = fence.char;
30
+ fenceCount = fence.count;
31
+ result.push(line);
32
+ continue;
33
+ }
34
+ const heading = HEADING_ID_RE.exec(line);
35
+ if ((heading === null || heading === void 0 ? void 0 : heading[1]) && heading[2] && heading[3]) {
36
+ const level = heading[1].length;
37
+ const sanitizedId = slugify(heading[3].trim(), { preserveCharacters: ['_'] });
38
+ result.push(`<h${level} id="${sanitizedId}">`, heading[2], `</h${level}>`);
39
+ continue;
40
+ }
41
+ result.push(line);
42
+ }
43
+ return result.join('\n');
44
+ }
45
+ function parseOpeningFence(stripped) {
46
+ const char = stripped[0];
47
+ if (char !== '`' && char !== '~')
48
+ return undefined;
49
+ let count = 1;
50
+ while (count < stripped.length && stripped[count] === char)
51
+ count++;
52
+ if (count < 3)
53
+ return undefined;
54
+ return { char, count };
55
+ }
56
+ function isClosingFence(stripped, fenceChar, fenceCount) {
57
+ let i = 0;
58
+ while (i < stripped.length && stripped[i] === fenceChar)
59
+ i++;
60
+ if (i < fenceCount)
61
+ return false;
62
+ for (; i < stripped.length; i++) {
63
+ if (stripped[i] !== ' ' && stripped[i] !== '\t' && stripped[i] !== '\r')
64
+ return false;
65
+ }
66
+ return true;
21
67
  }
68
+ export const ALL_HEADING_NAMES = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
22
69
  export const isJsxHeadingElement = (node) => {
23
70
  return (node.type === 'mdxJsxFlowElement' &&
24
71
  'name' in node &&
25
72
  typeof node.name === 'string' &&
26
- HEADING_NAMES.includes(node.name));
73
+ ALL_HEADING_NAMES.includes(node.name));
27
74
  };
@@ -1,6 +1,7 @@
1
1
  import { divisions, } from '@mintlify/validation';
2
2
  import { replaceSlashIndex } from '../slug/replaceSlashIndex.js';
3
3
  import { checkNavAccess, isPage } from './index.js';
4
+ import { prioritizeDefaultDivisionItem } from './prioritize-default-division-item.js';
4
5
  export function getFirstPageFromNavigation(node, userGroups, shouldCheckNavAccess = false, isPreview = false) {
5
6
  if (!node || typeof node !== 'object')
6
7
  return undefined;
@@ -44,7 +45,8 @@ export function getFirstPageFromNavigation(node, userGroups, shouldCheckNavAcces
44
45
  if (key in node) {
45
46
  const items = node[key];
46
47
  if (Array.isArray(items)) {
47
- for (const item of items) {
48
+ const orderedItems = prioritizeDefaultDivisionItem(items, key);
49
+ for (const item of orderedItems) {
48
50
  if (typeof item === 'object' && 'hidden' in item && item.hidden)
49
51
  continue;
50
52
  const page = getFirstPageFromNavigation(item, userGroups, shouldCheckNavAccess, isPreview);
@@ -6,3 +6,4 @@ export * from './generatePathToBreadcrumbsMapForDocsConfig.js';
6
6
  export * from './getAllPathsInDocsNav.js';
7
7
  export * from './checkNavAccess.js';
8
8
  export * from './getFirstPageFromNavConfig.js';
9
+ export * from './prioritize-default-division-item.js';
@@ -6,3 +6,4 @@ export * from './generatePathToBreadcrumbsMapForDocsConfig.js';
6
6
  export * from './getAllPathsInDocsNav.js';
7
7
  export * from './checkNavAccess.js';
8
8
  export * from './getFirstPageFromNavConfig.js';
9
+ export * from './prioritize-default-division-item.js';
@@ -0,0 +1 @@
1
+ export declare function prioritizeDefaultDivisionItem<T>(items: T[], key: string): T[];
@@ -0,0 +1,12 @@
1
+ const divisionsThatPrioritizeDefault = new Set(['languages', 'versions']);
2
+ function isDefaultDivisionItem(value) {
3
+ return typeof value === 'object' && value !== null && 'default' in value;
4
+ }
5
+ export function prioritizeDefaultDivisionItem(items, key) {
6
+ if (!divisionsThatPrioritizeDefault.has(key))
7
+ return items;
8
+ const defaultItem = items.find((item) => isDefaultDivisionItem(item) && item.default);
9
+ if (!defaultItem)
10
+ return items;
11
+ return [defaultItem, ...items.filter((item) => item !== defaultItem)];
12
+ }