@mintlify/common 1.0.1203 → 1.0.1205
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/getMDXOptions.d.ts +3 -1
- package/dist/mdx/getMDXOptions.js +3 -1
- package/dist/mdx/plugins/remark/remarkMdxRemoveJs.d.ts +4 -0
- package/dist/mdx/plugins/remark/remarkMdxRemoveJs.js +124 -11
- package/dist/mdx/server-only/getMdx.d.ts +2 -1
- package/dist/mdx/server-only/getMdx.js +23 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -4,6 +4,7 @@ import { DocsConfig } from '@mintlify/validation';
|
|
|
4
4
|
import type { Root } from 'mdast';
|
|
5
5
|
import type { PluggableList } from 'unified';
|
|
6
6
|
import { MdxExtracts } from '../types/mdx/MdxExtracts.js';
|
|
7
|
+
import { type MdxJsDetection } from './plugins/remark/remarkMdxRemoveJs.js';
|
|
7
8
|
type MDXOptionsData = {
|
|
8
9
|
snippetTreeMap: Record<string, Root>;
|
|
9
10
|
pageMetadata: PageMetaTags;
|
|
@@ -12,7 +13,7 @@ type MDXOptionsData = {
|
|
|
12
13
|
tailwindSelectors?: string[];
|
|
13
14
|
path?: string;
|
|
14
15
|
};
|
|
15
|
-
export declare const getMDXOptions: ({ data, remarkPlugins, earlyRemarkPlugins, rehypePlugins, mdxExtracts, allowedComponents, fragmentPlugins, }: {
|
|
16
|
+
export declare const getMDXOptions: ({ data, remarkPlugins, earlyRemarkPlugins, rehypePlugins, mdxExtracts, allowedComponents, fragmentPlugins, detectJs, }: {
|
|
16
17
|
data: MDXOptionsData;
|
|
17
18
|
remarkPlugins?: PluggableList;
|
|
18
19
|
earlyRemarkPlugins?: PluggableList;
|
|
@@ -24,5 +25,6 @@ export declare const getMDXOptions: ({ data, remarkPlugins, earlyRemarkPlugins,
|
|
|
24
25
|
remarkPlugins: PluggableList;
|
|
25
26
|
rehypePlugins: PluggableList;
|
|
26
27
|
};
|
|
28
|
+
detectJs?: MdxJsDetection;
|
|
27
29
|
}) => SerializeOptions["mdxOptions"];
|
|
28
30
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { rehypeCodeBlocks, rehypeDynamicTailwindCss, rehypeKeyboardSymbols, rehypeListItemText, rehypeMdxExtractEndpoint, rehypeMdxExtractExamples, rehypeParamFieldIds, rehypeRawComponents, rehypeTable, rehypeUnicodeIds, rehypeZoomImages, remarkExtractChangelogFilters, remarkExtractTableOfContents, remarkFileTree, remarkFrames, remarkComponentIds, remarkMdxInjectSnippets, remarkMdxStyleStringToObject, remarkMdxRemoveUnusedVariables, remarkRemoveImports, remarkMdxExtractPanel, remarkResolveRelativeLinks, remarkVideo, remarkMdxClientComponentBoundaries, remarkExtractMultiView, remarkPrompt, remarkUnwrapInlineJsx, remarkUnwrapJsxHeadings, remarkMapExplicitAnchors, } from './plugins/index.js';
|
|
2
2
|
import { remarkMdxExpandExpressions, } from './plugins/remark/remarkMdxExpandExpressions.js';
|
|
3
|
+
import { remarkMdxDetectJs } from './plugins/remark/remarkMdxRemoveJs.js';
|
|
3
4
|
import { remarkMdxRemoveUnknownJsx } from './plugins/remark/remarkMdxRemoveUnknownJsx/index.js';
|
|
4
5
|
import { remarkMermaid } from './plugins/remark/remarkMermaid.js';
|
|
5
6
|
import { remarkNoCopyLanguage } from './plugins/remark/remarkNoCopyLanguage.js';
|
|
@@ -13,7 +14,7 @@ const rehypeExtractors = (mdxExtracts, data) => {
|
|
|
13
14
|
[rehypeMdxExtractEndpoint, data.pageMetadata, data.config, mdxExtracts],
|
|
14
15
|
];
|
|
15
16
|
};
|
|
16
|
-
export const getMDXOptions = ({ data, remarkPlugins = [], earlyRemarkPlugins = [], rehypePlugins = [], mdxExtracts, allowedComponents = [], fragmentPlugins, }) => {
|
|
17
|
+
export const getMDXOptions = ({ data, remarkPlugins = [], earlyRemarkPlugins = [], rehypePlugins = [], mdxExtracts, allowedComponents = [], fragmentPlugins, detectJs, }) => {
|
|
17
18
|
// `fragment`: the list is compiling the body of an `<MDX>` inside an expression. Page-level
|
|
18
19
|
// extractors that hoist an element out of the tree (`<Panel>`, `<RequestExample>`,
|
|
19
20
|
// `<ResponseExample>`) are skipped there so the element stays in its branch and renders inline
|
|
@@ -55,6 +56,7 @@ export const getMDXOptions = ({ data, remarkPlugins = [], earlyRemarkPlugins = [
|
|
|
55
56
|
remarkMermaid,
|
|
56
57
|
remarkNoCopyLanguage,
|
|
57
58
|
remarkVideo,
|
|
59
|
+
...(fragment || !detectJs ? [] : [[remarkMdxDetectJs, detectJs]]),
|
|
58
60
|
...remarkPlugins,
|
|
59
61
|
remarkRenderTitleMarkdown,
|
|
60
62
|
remarkMapExplicitAnchors,
|
|
@@ -8,3 +8,7 @@ export declare function isPureDataExpression(value: unknown, depth?: number): bo
|
|
|
8
8
|
export declare function isPureDataProgram(estree: Program): boolean;
|
|
9
9
|
export declare function isUnsafeDataAttributeName(name: string): boolean;
|
|
10
10
|
export declare function remarkMdxRemoveJs(): (tree: Root) => void;
|
|
11
|
+
export type MdxJsDetection = {
|
|
12
|
+
hasJs: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function remarkMdxDetectJs(detection: MdxJsDetection): (tree: Root) => void;
|
|
@@ -570,7 +570,106 @@ function tryResolveAsString(estree, constants) {
|
|
|
570
570
|
const val = constants.get(expr.name);
|
|
571
571
|
return val !== undefined ? val : null;
|
|
572
572
|
}
|
|
573
|
+
const isMdxExpression = (node) => node.type === 'mdxTextExpression' || node.type === 'mdxFlowExpression';
|
|
574
|
+
// mdx comments ({/* … */}) parse to an expression with an empty estree body.
|
|
575
|
+
// the raw source must also be comment/whitespace-only: empty-body alone is an
|
|
576
|
+
// undocumented parser detail a toolchain bump could silently widen
|
|
577
|
+
// linear scan: a regex over repeated block comments backtracks exponentially
|
|
578
|
+
function isBlockCommentsAndWhitespace(value) {
|
|
579
|
+
let index = 0;
|
|
580
|
+
while (index < value.length) {
|
|
581
|
+
if (/\s/.test(value.charAt(index))) {
|
|
582
|
+
index++;
|
|
583
|
+
}
|
|
584
|
+
else if (value.startsWith('/*', index)) {
|
|
585
|
+
const end = value.indexOf('*/', index + 2);
|
|
586
|
+
if (end === -1)
|
|
587
|
+
return false;
|
|
588
|
+
index = end + 2;
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
function isCommentOnlyExpression(node) {
|
|
597
|
+
var _a, _b, _c;
|
|
598
|
+
const estree = (_a = node.data) === null || _a === void 0 ? void 0 : _a.estree;
|
|
599
|
+
return (estree != null &&
|
|
600
|
+
estree.body.length === 0 &&
|
|
601
|
+
((_b = estree.comments) !== null && _b !== void 0 ? _b : []).every((comment) => comment.type === 'Block' && !comment.value.includes('*/')) &&
|
|
602
|
+
isBlockCommentsAndWhitespace((_c = node.value) !== null && _c !== void 0 ? _c : ''));
|
|
603
|
+
}
|
|
604
|
+
// prettier emits {" "} to preserve spaces between inline elements. whitespace-only
|
|
605
|
+
// string literals are inert in every render position (unlike arbitrary string
|
|
606
|
+
// literals, which would become executable text inside kept <script> children),
|
|
607
|
+
// so both passes keep them and rendering stays byte-identical by construction
|
|
608
|
+
function isWhitespaceStringExpression(node) {
|
|
609
|
+
var _a;
|
|
610
|
+
const estree = (_a = node.data) === null || _a === void 0 ? void 0 : _a.estree;
|
|
611
|
+
if (estree == null || estree.body.length !== 1)
|
|
612
|
+
return false;
|
|
613
|
+
const stmt = estree.body[0];
|
|
614
|
+
if ((stmt === null || stmt === void 0 ? void 0 : stmt.type) !== 'ExpressionStatement')
|
|
615
|
+
return false;
|
|
616
|
+
const expr = stmt.expression;
|
|
617
|
+
return (expr.type === 'Literal' &&
|
|
618
|
+
typeof expr.value === 'string' &&
|
|
619
|
+
expr.value.trim() === '' &&
|
|
620
|
+
typeof expr.raw === 'string' &&
|
|
621
|
+
/^(["'])(?:\s|\\[nrt])*\1$/.test(expr.raw));
|
|
622
|
+
}
|
|
623
|
+
function isInertExpression(node) {
|
|
624
|
+
return isCommentOnlyExpression(node) || isWhitespaceStringExpression(node);
|
|
625
|
+
}
|
|
626
|
+
const applyEdits = {
|
|
627
|
+
replaceChild: (parent, index, node) => {
|
|
628
|
+
parent.children[index] = node;
|
|
629
|
+
},
|
|
630
|
+
remove: (tree, test) => {
|
|
631
|
+
remove(tree, test);
|
|
632
|
+
},
|
|
633
|
+
setAttributes: (node, attributes) => {
|
|
634
|
+
node.attributes = attributes;
|
|
635
|
+
},
|
|
636
|
+
rewriteAttribute: (value, raw, estree) => {
|
|
637
|
+
value.value = raw;
|
|
638
|
+
value.data = Object.assign(Object.assign({}, value.data), { estree });
|
|
639
|
+
},
|
|
640
|
+
prepend: (tree, node) => {
|
|
641
|
+
tree.children.unshift(node);
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
const WOULD_CHANGE = new Error('remarkMdxRemoveJs would change the tree');
|
|
645
|
+
const detectEdits = {
|
|
646
|
+
replaceChild: () => {
|
|
647
|
+
throw WOULD_CHANGE;
|
|
648
|
+
},
|
|
649
|
+
remove: (tree, test) => {
|
|
650
|
+
visit(tree, (node) => {
|
|
651
|
+
if (test(node))
|
|
652
|
+
throw WOULD_CHANGE;
|
|
653
|
+
});
|
|
654
|
+
},
|
|
655
|
+
setAttributes: (node, attributes) => {
|
|
656
|
+
if (attributes.length !== node.attributes.length ||
|
|
657
|
+
attributes.some((attr, i) => attr !== node.attributes[i]))
|
|
658
|
+
throw WOULD_CHANGE;
|
|
659
|
+
},
|
|
660
|
+
rewriteAttribute: () => {
|
|
661
|
+
throw WOULD_CHANGE;
|
|
662
|
+
},
|
|
663
|
+
prepend: () => {
|
|
664
|
+
throw WOULD_CHANGE;
|
|
665
|
+
},
|
|
666
|
+
};
|
|
573
667
|
export function remarkMdxRemoveJs() {
|
|
668
|
+
return sanitizer(applyEdits);
|
|
669
|
+
}
|
|
670
|
+
// change the tree only through edit: remarkMdxDetectJs only sees edits, so a direct mutation
|
|
671
|
+
// would skip the no-js compile and serve unsanitized output
|
|
672
|
+
function sanitizer(edit) {
|
|
574
673
|
return (tree) => {
|
|
575
674
|
const constantStrings = collectStringConstants(tree);
|
|
576
675
|
if (constantStrings.size > 0) {
|
|
@@ -587,10 +686,10 @@ export function remarkMdxRemoveJs() {
|
|
|
587
686
|
const replacement = node.type === 'mdxTextExpression'
|
|
588
687
|
? { type: 'text', value: substitution }
|
|
589
688
|
: { type: 'paragraph', children: [{ type: 'text', value: substitution }] };
|
|
590
|
-
parent
|
|
689
|
+
edit.replaceChild(parent, index, replacement);
|
|
591
690
|
});
|
|
592
691
|
}
|
|
593
|
-
remove(tree,
|
|
692
|
+
edit.remove(tree, (node) => isMdxExpression(node) && !isInertExpression(node));
|
|
594
693
|
// Collect exported names before stripping ESM so we can inject stubs.
|
|
595
694
|
// Map keyed by name deduplicates naturally.
|
|
596
695
|
const stubbedNamesMap = new Map();
|
|
@@ -603,7 +702,7 @@ export function remarkMdxRemoveJs() {
|
|
|
603
702
|
}
|
|
604
703
|
}
|
|
605
704
|
});
|
|
606
|
-
remove(tree, (node) => isMdxJsEsm(node));
|
|
705
|
+
edit.remove(tree, (node) => isMdxJsEsm(node));
|
|
607
706
|
// Sanitize JSX attributes. Any bare identifier that survives is added to stubbedNamesMap
|
|
608
707
|
// so it gets a stub even if its import was already stripped by a prior plugin.
|
|
609
708
|
visit(tree, (node) => {
|
|
@@ -634,28 +733,42 @@ export function remarkMdxRemoveJs() {
|
|
|
634
733
|
const stmt = filteredEstree.body[0];
|
|
635
734
|
if ((stmt === null || stmt === void 0 ? void 0 : stmt.type) === 'ExpressionStatement' &&
|
|
636
735
|
stmt.expression.type === 'ObjectExpression') {
|
|
637
|
-
attr.value
|
|
638
|
-
attr.value.data.estree = filteredEstree;
|
|
736
|
+
edit.rewriteAttribute(attr.value, rebuildStyleValue(stmt.expression.properties), filteredEstree);
|
|
639
737
|
return attr;
|
|
640
738
|
}
|
|
641
739
|
}
|
|
642
740
|
return undefined;
|
|
643
741
|
}
|
|
644
742
|
if (Object.keys(DEFAULT_PROP_EXPRESSIONS).includes(attr.name)) {
|
|
645
|
-
attr.value
|
|
646
|
-
attr.value.data = {
|
|
647
|
-
estree: structuredClone((_h = (_g = DEFAULT_PROP_EXPRESSIONS[attr.name]) === null || _g === void 0 ? void 0 : _g.estree) !== null && _h !== void 0 ? _h : objectEstree),
|
|
648
|
-
};
|
|
743
|
+
edit.rewriteAttribute(attr.value, (_f = (_e = DEFAULT_PROP_EXPRESSIONS[attr.name]) === null || _e === void 0 ? void 0 : _e.value) !== null && _f !== void 0 ? _f : '{}', structuredClone((_h = (_g = DEFAULT_PROP_EXPRESSIONS[attr.name]) === null || _g === void 0 ? void 0 : _g.estree) !== null && _h !== void 0 ? _h : objectEstree));
|
|
649
744
|
return attr;
|
|
650
745
|
}
|
|
651
746
|
}
|
|
652
747
|
return undefined;
|
|
653
748
|
});
|
|
654
|
-
node
|
|
749
|
+
edit.setAttributes(node, newAttributes.filter((attr) => attr !== undefined));
|
|
655
750
|
});
|
|
656
751
|
// Inject stubs after the attribute walk so identifiers collected from attributes are included
|
|
657
752
|
if (stubbedNamesMap.size > 0) {
|
|
658
|
-
|
|
753
|
+
edit.prepend(tree, buildStubEsmNode([...stubbedNamesMap.values()]));
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
// dry-run twin of remarkMdxRemoveJs for the with-js compile pass: reports
|
|
758
|
+
// whether the sanitizer would change this tree, without touching it. a false
|
|
759
|
+
// detection proves the no-js compile would be byte-identical and can be skipped
|
|
760
|
+
export function remarkMdxDetectJs(detection) {
|
|
761
|
+
const detect = sanitizer(detectEdits);
|
|
762
|
+
return (tree) => {
|
|
763
|
+
let hasJs = false;
|
|
764
|
+
try {
|
|
765
|
+
detect(tree);
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
if (error !== WOULD_CHANGE)
|
|
769
|
+
throw error;
|
|
770
|
+
hasJs = true;
|
|
659
771
|
}
|
|
772
|
+
detection.hasJs = hasJs;
|
|
660
773
|
};
|
|
661
774
|
}
|
|
@@ -13,7 +13,7 @@ export type GetMdxType = {
|
|
|
13
13
|
panelMdxSourceWithNoJs?: SerializeSuccess;
|
|
14
14
|
parseFailed?: boolean;
|
|
15
15
|
};
|
|
16
|
-
export declare function getMdx({ path, content, metadata, snippets, subdomain, codeStyling, config, tailwindSelectors, pageType, trace, customLanguages, variables, rehypePlugins, remarkPlugins, earlyRemarkPlugins, allowedComponents, }: {
|
|
16
|
+
export declare function getMdx({ path, content, metadata, snippets, subdomain, codeStyling, config, tailwindSelectors, pageType, trace, customLanguages, variables, rehypePlugins, remarkPlugins, earlyRemarkPlugins, allowedComponents, singlePass, }: {
|
|
17
17
|
path: string;
|
|
18
18
|
content: string;
|
|
19
19
|
metadata: PageMetaTags;
|
|
@@ -30,5 +30,6 @@ export declare function getMdx({ path, content, metadata, snippets, subdomain, c
|
|
|
30
30
|
remarkPlugins?: PluggableList;
|
|
31
31
|
earlyRemarkPlugins?: PluggableList;
|
|
32
32
|
allowedComponents?: string[];
|
|
33
|
+
singlePass?: boolean;
|
|
33
34
|
}): Promise<GetMdxType>;
|
|
34
35
|
export { createSnippetTreeMap, type Snippet };
|
|
@@ -17,7 +17,7 @@ import { replaceVariables } from '../replaceVariables.js';
|
|
|
17
17
|
import { createSnippetTreeMap } from './getMdx/snippets.js';
|
|
18
18
|
import { getSerializeExtras } from './getSerializeExtras.js';
|
|
19
19
|
export function getMdx(_a) {
|
|
20
|
-
return __awaiter(this, arguments, void 0, function* ({ path, content, metadata, snippets, subdomain, codeStyling, config, tailwindSelectors = undefined, pageType = 'default', trace = undefined, customLanguages = [], variables = undefined, rehypePlugins = [], remarkPlugins = [], earlyRemarkPlugins = [], allowedComponents = [], }) {
|
|
20
|
+
return __awaiter(this, arguments, void 0, function* ({ path, content, metadata, snippets, subdomain, codeStyling, config, tailwindSelectors = undefined, pageType = 'default', trace = undefined, customLanguages = [], variables = undefined, rehypePlugins = [], remarkPlugins = [], earlyRemarkPlugins = [], allowedComponents = [], singlePass = true, }) {
|
|
21
21
|
const traceFn = trace !== null && trace !== void 0 ? trace : ((_name, fn) => fn());
|
|
22
22
|
const processedContent = preprocessCustomHeadingIds(preprocessMdxFragmentCode(replaceVariables(content, variables)));
|
|
23
23
|
const processedSnippets = snippets.map((snippet) => (Object.assign(Object.assign({}, snippet), { content: preprocessCustomHeadingIds(preprocessMdxFragmentCode(replaceVariables(snippet.content, variables))) })));
|
|
@@ -46,12 +46,16 @@ export function getMdx(_a) {
|
|
|
46
46
|
plugins = [...plugins, remarkExpandContent, remarkSplitCodeGroup, remarkSplitTabs];
|
|
47
47
|
}
|
|
48
48
|
const syntaxHighlightingOptions = Object.assign(Object.assign({}, codeStylingToThemeOrThemes(codeStyling)), { customLanguages });
|
|
49
|
+
// detector sits exactly where remarkMdxRemoveJs sits in the no-js pass, so it
|
|
50
|
+
// sees the identical tree state and applies the identical decisions
|
|
51
|
+
const jsDetection = { hasJs: true };
|
|
49
52
|
const mdxOptions = getMDXOptions({
|
|
50
53
|
data: mdxOptionsData,
|
|
51
54
|
remarkPlugins: plugins,
|
|
52
55
|
earlyRemarkPlugins,
|
|
53
56
|
rehypePlugins,
|
|
54
57
|
mdxExtracts,
|
|
58
|
+
detectJs: jsDetection,
|
|
55
59
|
allowedComponents,
|
|
56
60
|
fragmentPlugins: getSerializeExtras(syntaxHighlightingOptions),
|
|
57
61
|
});
|
|
@@ -78,6 +82,7 @@ export function getMdx(_a) {
|
|
|
78
82
|
path,
|
|
79
83
|
traceFn,
|
|
80
84
|
customLanguages,
|
|
85
|
+
skipNoJs: singlePass ? { detection: jsDetection, mdxExtracts } : undefined,
|
|
81
86
|
});
|
|
82
87
|
}));
|
|
83
88
|
let parseFailed = mainParseFailed;
|
|
@@ -117,7 +122,7 @@ export function getMdx(_a) {
|
|
|
117
122
|
});
|
|
118
123
|
}
|
|
119
124
|
function compileMdx(_a) {
|
|
120
|
-
return __awaiter(this, arguments, void 0, function* ({ content, scope, mdxOptions, mdxOptionsNoJs, codeStyling, path, traceFn, customLanguages, }) {
|
|
125
|
+
return __awaiter(this, arguments, void 0, function* ({ content, scope, mdxOptions, mdxOptionsNoJs, codeStyling, path, traceFn, customLanguages, skipNoJs, }) {
|
|
121
126
|
const withJs = yield traceFn('compileMdx.withJs', () => __awaiter(this, void 0, void 0, function* () {
|
|
122
127
|
return getCompiledMdxWithFallback({
|
|
123
128
|
source: content,
|
|
@@ -129,6 +134,22 @@ function compileMdx(_a) {
|
|
|
129
134
|
customLanguages,
|
|
130
135
|
});
|
|
131
136
|
}));
|
|
137
|
+
// the two passes differ only in remarkMdxRemoveJs (detector reported a no-op)
|
|
138
|
+
// and rehypeMdxExtractExamples splicing (nothing was collected), so the no-js
|
|
139
|
+
// compile would be byte-identical: reuse the with-js result
|
|
140
|
+
const examples = skipNoJs === null || skipNoJs === void 0 ? void 0 : skipNoJs.mdxExtracts.codeExamples;
|
|
141
|
+
const canSkip = skipNoJs !== undefined &&
|
|
142
|
+
!withJs.parseFailed &&
|
|
143
|
+
!skipNoJs.detection.hasJs &&
|
|
144
|
+
!(examples === null || examples === void 0 ? void 0 : examples.request) &&
|
|
145
|
+
!(examples === null || examples === void 0 ? void 0 : examples.response);
|
|
146
|
+
if (canSkip) {
|
|
147
|
+
return {
|
|
148
|
+
mdxSource: withJs.result,
|
|
149
|
+
mdxSourceWithNoJs: withJs.result,
|
|
150
|
+
parseFailed: withJs.parseFailed,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
132
153
|
const withoutJs = yield traceFn('compileMdx.withoutJs', () => __awaiter(this, void 0, void 0, function* () {
|
|
133
154
|
return getCompiledMdxWithFallback({
|
|
134
155
|
source: content,
|