@mintlify/common 1.0.1118 → 1.0.1119
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/remark/remarkMdxExpandExpressions.d.ts +4 -3
- package/dist/mdx/plugins/remark/remarkMdxExpandExpressions.js +4 -3
- package/dist/mdx/preprocessCustomHeadingIds.d.ts +5 -0
- package/dist/mdx/preprocessCustomHeadingIds.js +17 -2
- package/dist/mdx/preprocessCustomHeadingIds.test.js +34 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -71,9 +71,10 @@ export declare const MAX_MDX_FRAGMENTS_PER_PAGE = 500;
|
|
|
71
71
|
* `<p>` wrapper is unwrapped so it does not nest in the surrounding paragraph.
|
|
72
72
|
*
|
|
73
73
|
* Escaping: the fragment source is JSX text, so a literal brace must be written the JSX way,
|
|
74
|
-
* e.g. `{'{'}` / `{'}'}`; that expression is then evaluated by MDX and renders the brace.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* e.g. `{'{'}` / `{'}'}`; that expression is then evaluated by MDX and renders the brace. A custom
|
|
75
|
+
* heading anchor (`## Title {#id}`) is fine: `preprocessCustomHeadingIds` rewrites it before the
|
|
76
|
+
* expression is parsed, at any indent inside `<MDX>`. Nothing beyond dedenting is done to the
|
|
77
|
+
* recovered text.
|
|
77
78
|
*
|
|
78
79
|
* Components defined on the page (`export const Local = () => …`, and named snippet imports,
|
|
79
80
|
* which prebuild rewrites into page exports) are passed into the fragment pipeline as known
|
|
@@ -61,9 +61,10 @@ export const MAX_MDX_FRAGMENTS_PER_PAGE = 500;
|
|
|
61
61
|
* `<p>` wrapper is unwrapped so it does not nest in the surrounding paragraph.
|
|
62
62
|
*
|
|
63
63
|
* Escaping: the fragment source is JSX text, so a literal brace must be written the JSX way,
|
|
64
|
-
* e.g. `{'{'}` / `{'}'}`; that expression is then evaluated by MDX and renders the brace.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* e.g. `{'{'}` / `{'}'}`; that expression is then evaluated by MDX and renders the brace. A custom
|
|
65
|
+
* heading anchor (`## Title {#id}`) is fine: `preprocessCustomHeadingIds` rewrites it before the
|
|
66
|
+
* expression is parsed, at any indent inside `<MDX>`. Nothing beyond dedenting is done to the
|
|
67
|
+
* recovered text.
|
|
67
68
|
*
|
|
68
69
|
* Components defined on the page (`export const Local = () => …`, and named snippet imports,
|
|
69
70
|
* which prebuild rewrites into page exports) are passed into the fragment pipeline as known
|
|
@@ -16,6 +16,11 @@ export interface PreprocessCustomHeadingIdsOptions {
|
|
|
16
16
|
*
|
|
17
17
|
* Uses a line-by-line scan with fence state tracking to skip headings inside
|
|
18
18
|
* code blocks, avoiding backtracking over large documents.
|
|
19
|
+
*
|
|
20
|
+
* Headings are matched with at most 3 spaces of indent (deeper is an indented code block),
|
|
21
|
+
* except inside an `<MDX>` block — including one nested in a `{…}` expression, where authors
|
|
22
|
+
* indent the body — whose content is dedented before parsing, so any indent is accepted there.
|
|
23
|
+
* Without this the `{#id}` would reach the expression parser as a JSX expression and fail.
|
|
19
24
|
*/
|
|
20
25
|
export declare function preprocessCustomHeadingIds(content: string, options?: PreprocessCustomHeadingIdsOptions): string;
|
|
21
26
|
export interface JsxHeadingElement extends MdxJsxFlowElement {
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
const HEADING_ID_RE = /^( {0,3})(#{1,6})\s+(.+?)\s*\{\s*#([^}]+?)\s*\}\s*$/;
|
|
2
|
+
/**
|
|
3
|
+
* Inside an `<MDX>` block the body is dedented before it is parsed, so indentation carries no
|
|
4
|
+
* meaning there (4+ spaces is not an indented code block) and any indent may precede the heading.
|
|
5
|
+
*/
|
|
6
|
+
const MDX_HEADING_ID_RE = /^(\s*)(#{1,6})\s+(.+?)\s*\{\s*#([^}]+?)\s*\}\s*$/;
|
|
7
|
+
// `<MDX>` or `<MDX className="x">`; not `<MDX />` (no body) and not `<MDXProvider>`
|
|
8
|
+
const MDX_OPEN_RE = /<MDX(?:\s[^>]*[^/>\s])?\s*>/g;
|
|
9
|
+
const MDX_CLOSE_RE = /<\/MDX\s*>/g;
|
|
2
10
|
const UNSAFE_HTML_ATTR_CHARS = /[<>"]/g;
|
|
3
11
|
/**
|
|
4
12
|
* Characters to strip from custom heading IDs in the default (non-editor) path.
|
|
@@ -23,13 +31,19 @@ function normalizeCustomId(raw) {
|
|
|
23
31
|
*
|
|
24
32
|
* Uses a line-by-line scan with fence state tracking to skip headings inside
|
|
25
33
|
* code blocks, avoiding backtracking over large documents.
|
|
34
|
+
*
|
|
35
|
+
* Headings are matched with at most 3 spaces of indent (deeper is an indented code block),
|
|
36
|
+
* except inside an `<MDX>` block — including one nested in a `{…}` expression, where authors
|
|
37
|
+
* indent the body — whose content is dedented before parsing, so any indent is accepted there.
|
|
38
|
+
* Without this the `{#id}` would reach the expression parser as a JSX expression and fail.
|
|
26
39
|
*/
|
|
27
40
|
export function preprocessCustomHeadingIds(content, options) {
|
|
28
|
-
var _a;
|
|
41
|
+
var _a, _b, _c, _d, _e;
|
|
29
42
|
const lines = content.split('\n');
|
|
30
43
|
const result = [];
|
|
31
44
|
let fenceChar;
|
|
32
45
|
let fenceCount = 0;
|
|
46
|
+
let mdxDepth = 0;
|
|
33
47
|
for (const line of lines) {
|
|
34
48
|
const stripped = line.trimStart();
|
|
35
49
|
if (fenceChar !== undefined) {
|
|
@@ -46,7 +60,7 @@ export function preprocessCustomHeadingIds(content, options) {
|
|
|
46
60
|
result.push(line);
|
|
47
61
|
continue;
|
|
48
62
|
}
|
|
49
|
-
const heading = HEADING_ID_RE.exec(line);
|
|
63
|
+
const heading = (mdxDepth > 0 ? MDX_HEADING_ID_RE : HEADING_ID_RE).exec(line);
|
|
50
64
|
if ((heading === null || heading === void 0 ? void 0 : heading[2]) && heading[3] && heading[4]) {
|
|
51
65
|
const indent = (_a = heading[1]) !== null && _a !== void 0 ? _a : '';
|
|
52
66
|
const level = heading[2].length;
|
|
@@ -56,6 +70,7 @@ export function preprocessCustomHeadingIds(content, options) {
|
|
|
56
70
|
result.push(`${indent}<h${level} id="${sanitizedId}">`, `${indent}${heading[3]}`, `${indent}</h${level}>`);
|
|
57
71
|
continue;
|
|
58
72
|
}
|
|
73
|
+
mdxDepth = Math.max(0, mdxDepth + ((_c = (_b = line.match(MDX_OPEN_RE)) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) - ((_e = (_d = line.match(MDX_CLOSE_RE)) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0));
|
|
59
74
|
result.push(line);
|
|
60
75
|
}
|
|
61
76
|
return result.join('\n');
|
|
@@ -23,6 +23,40 @@ describe('preprocessCustomHeadingIds', () => {
|
|
|
23
23
|
const input = ' ## Heading {#custom-id}';
|
|
24
24
|
expect(preprocessCustomHeadingIds(input)).toBe(input);
|
|
25
25
|
});
|
|
26
|
+
it('converts headings at any indent inside an <MDX> block', () => {
|
|
27
|
+
const input = `{variant === "fos" ? (
|
|
28
|
+
<MDX>
|
|
29
|
+
### Request a token {#request-token}
|
|
30
|
+
|
|
31
|
+
Body
|
|
32
|
+
</MDX>
|
|
33
|
+
) : null}
|
|
34
|
+
|
|
35
|
+
## Still code {#not-converted}`;
|
|
36
|
+
const output = preprocessCustomHeadingIds(input);
|
|
37
|
+
expect(output).toContain(' <h3 id="request-token">\n Request a token\n </h3>');
|
|
38
|
+
// depth is back to 0 after </MDX>: 4-space indent is an indented code block again
|
|
39
|
+
expect(output).toContain(' ## Still code {#not-converted}');
|
|
40
|
+
});
|
|
41
|
+
it('leaves code fences inside an <MDX> block alone', () => {
|
|
42
|
+
const input = `<MDX>
|
|
43
|
+
\`\`\`md
|
|
44
|
+
## Heading {#custom-id}
|
|
45
|
+
\`\`\`
|
|
46
|
+
</MDX>`;
|
|
47
|
+
expect(preprocessCustomHeadingIds(input)).toBe(input);
|
|
48
|
+
});
|
|
49
|
+
it('ignores an unrelated MDX-prefixed tag', () => {
|
|
50
|
+
const input = '<MDXProvider>\n ## Heading {#custom-id}\n</MDXProvider>';
|
|
51
|
+
expect(preprocessCustomHeadingIds(input)).toBe(input);
|
|
52
|
+
});
|
|
53
|
+
it('does not treat a self-closing <MDX /> as opening a block', () => {
|
|
54
|
+
// a self-closing tag has no body; the any-indent rule must not stay on for the rest of the file
|
|
55
|
+
for (const tag of ['<MDX />', '<MDX/>', '<MDX className="x" />']) {
|
|
56
|
+
const input = `${tag}\n\n ## Still code {#not-converted}`;
|
|
57
|
+
expect(preprocessCustomHeadingIds(input)).toBe(input);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
26
60
|
it('leaves tab-indented headings alone', () => {
|
|
27
61
|
const input = '\t## Heading {#custom-id}';
|
|
28
62
|
expect(preprocessCustomHeadingIds(input)).toBe(input);
|