@docusaurus/mdx-loader 0.0.0-5553 → 0.0.0-5556
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/lib/frontMatter.d.ts +12 -0
- package/lib/frontMatter.d.ts.map +1 -0
- package/lib/frontMatter.js +25 -0
- package/lib/frontMatter.js.map +1 -0
- package/lib/index.d.ts +2 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +3 -5
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +90 -22
- package/lib/loader.js.map +1 -1
- package/lib/preprocessor.d.ts +18 -0
- package/lib/preprocessor.d.ts.map +1 -0
- package/lib/preprocessor.js +34 -0
- package/lib/preprocessor.js.map +1 -0
- package/lib/remark/admonitions/index.d.ts +2 -8
- package/lib/remark/admonitions/index.d.ts.map +1 -1
- package/lib/remark/admonitions/index.js +61 -129
- package/lib/remark/admonitions/index.js.map +1 -1
- package/lib/remark/details/index.d.ts +9 -0
- package/lib/remark/details/index.d.ts.map +1 -0
- package/lib/remark/details/index.js +23 -0
- package/lib/remark/details/index.js.map +1 -0
- package/lib/remark/head/index.d.ts +9 -0
- package/lib/remark/head/index.d.ts.map +1 -0
- package/lib/remark/head/index.js +23 -0
- package/lib/remark/head/index.js.map +1 -0
- package/lib/remark/headings/index.d.ts.map +1 -1
- package/lib/remark/headings/index.js +3 -3
- package/lib/remark/headings/index.js.map +1 -1
- package/lib/remark/{unwrapMdxCodeBlocks/index.d.ts → mdx1Compat/codeCompatPlugin.d.ts} +2 -2
- package/lib/remark/mdx1Compat/codeCompatPlugin.d.ts.map +1 -0
- package/lib/remark/mdx1Compat/codeCompatPlugin.js +34 -0
- package/lib/remark/mdx1Compat/codeCompatPlugin.js.map +1 -0
- package/lib/remark/mermaid/index.d.ts.map +1 -1
- package/lib/remark/mermaid/index.js +1 -0
- package/lib/remark/mermaid/index.js.map +1 -1
- package/lib/remark/toc/index.d.ts +8 -2
- package/lib/remark/toc/index.d.ts.map +1 -1
- package/lib/remark/toc/index.js +53 -23
- package/lib/remark/toc/index.js.map +1 -1
- package/lib/remark/transformImage/index.d.ts +2 -1
- package/lib/remark/transformImage/index.d.ts.map +1 -1
- package/lib/remark/transformImage/index.js +42 -15
- package/lib/remark/transformImage/index.js.map +1 -1
- package/lib/remark/transformLinks/index.d.ts +2 -1
- package/lib/remark/transformLinks/index.d.ts.map +1 -1
- package/lib/remark/transformLinks/index.js +30 -11
- package/lib/remark/transformLinks/index.js.map +1 -1
- package/lib/remark/utils/index.d.ts +4 -2
- package/lib/remark/utils/index.d.ts.map +1 -1
- package/lib/remark/utils/index.js +68 -11
- package/lib/remark/utils/index.js.map +1 -1
- package/package.json +17 -10
- package/src/frontMatter.ts +31 -0
- package/src/index.ts +3 -5
- package/src/loader.ts +137 -33
- package/src/preprocessor.ts +47 -0
- package/src/remark/admonitions/index.ts +80 -176
- package/src/remark/details/index.ts +25 -0
- package/src/remark/head/index.ts +25 -0
- package/src/remark/headings/index.ts +5 -3
- package/src/remark/mdx1Compat/codeCompatPlugin.ts +36 -0
- package/src/remark/mermaid/index.ts +2 -0
- package/src/remark/toc/index.ts +80 -26
- package/src/remark/transformImage/index.ts +54 -19
- package/src/remark/transformLinks/index.ts +44 -17
- package/src/remark/utils/index.ts +77 -9
- package/lib/remark/unwrapMdxCodeBlocks/index.d.ts.map +0 -1
- package/lib/remark/unwrapMdxCodeBlocks/index.js +0 -29
- package/lib/remark/unwrapMdxCodeBlocks/index.js.map +0 -1
- package/src/deps.d.ts +0 -31
- package/src/remark/unwrapMdxCodeBlocks/index.ts +0 -33
|
@@ -4,25 +4,26 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
7
|
import visit from 'unist-util-visit';
|
|
9
|
-
|
|
10
|
-
import type {
|
|
8
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
9
|
+
import type {Transformer, Processor} from 'unified';
|
|
10
|
+
|
|
11
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
12
|
+
import type {ContainerDirective} from 'mdast-util-directive';
|
|
13
|
+
import type {Parent} from 'mdast';
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
// TODO as of April 2023, no way to import/re-export this ESM type easily :/
|
|
16
|
+
// This might change soon, likely after TS 5.2
|
|
17
|
+
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
|
|
18
|
+
// import type {Plugin} from 'unified';
|
|
19
|
+
type Plugin = any; // TODO fix this asap
|
|
13
20
|
|
|
14
|
-
// TODO not ideal option shape
|
|
15
|
-
// First let upgrade to MDX 2.0
|
|
16
|
-
// Maybe we'll want to provide different tags for different admonition types?
|
|
17
|
-
// Also maybe rename "keywords" to "types"?
|
|
18
21
|
export type AdmonitionOptions = {
|
|
19
|
-
tag: string;
|
|
20
22
|
keywords: string[];
|
|
21
23
|
extendDefaults: boolean;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export const DefaultAdmonitionOptions: AdmonitionOptions = {
|
|
25
|
-
tag: ':::',
|
|
26
27
|
keywords: [
|
|
27
28
|
'secondary',
|
|
28
29
|
'info',
|
|
@@ -34,16 +35,16 @@ export const DefaultAdmonitionOptions: AdmonitionOptions = {
|
|
|
34
35
|
'important',
|
|
35
36
|
'caution',
|
|
36
37
|
],
|
|
37
|
-
extendDefaults:
|
|
38
|
+
extendDefaults: true,
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function normalizeOptions(
|
|
45
|
-
providedOptions: Partial<AdmonitionOptions>,
|
|
41
|
+
export function normalizeAdmonitionOptions(
|
|
42
|
+
providedOptions: Partial<AdmonitionOptions> | true,
|
|
46
43
|
): AdmonitionOptions {
|
|
44
|
+
if (providedOptions === true) {
|
|
45
|
+
return DefaultAdmonitionOptions;
|
|
46
|
+
}
|
|
47
|
+
|
|
47
48
|
const options = {...DefaultAdmonitionOptions, ...providedOptions};
|
|
48
49
|
|
|
49
50
|
// By default it makes more sense to append keywords to the default ones
|
|
@@ -58,181 +59,84 @@ function normalizeOptions(
|
|
|
58
59
|
return options;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
type DirectiveLabel = Parent;
|
|
63
|
+
type DirectiveContent = ContainerDirective['children'];
|
|
64
|
+
|
|
65
|
+
function parseDirective(directive: ContainerDirective): {
|
|
66
|
+
directiveLabel: DirectiveLabel | undefined;
|
|
67
|
+
contentNodes: DirectiveContent;
|
|
68
|
+
} {
|
|
69
|
+
const hasDirectiveLabel =
|
|
70
|
+
directive.children?.[0]?.data?.directiveLabel === true;
|
|
71
|
+
if (hasDirectiveLabel) {
|
|
72
|
+
const [directiveLabel, ...contentNodes] = directive.children;
|
|
73
|
+
return {directiveLabel: directiveLabel as DirectiveLabel, contentNodes};
|
|
74
|
+
}
|
|
75
|
+
return {directiveLabel: undefined, contentNodes: directive.children};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getTextOnlyTitle(directiveLabel: DirectiveLabel): string | undefined {
|
|
79
|
+
const isTextOnlyTitle =
|
|
80
|
+
directiveLabel?.children?.length === 1 &&
|
|
81
|
+
directiveLabel?.children?.[0]?.type === 'text';
|
|
82
|
+
return isTextOnlyTitle
|
|
83
|
+
? // @ts-expect-error: todo type
|
|
84
|
+
(directiveLabel?.children?.[0].value as string)
|
|
85
|
+
: undefined;
|
|
86
|
+
}
|
|
64
87
|
|
|
65
88
|
const plugin: Plugin = function plugin(
|
|
66
89
|
this: Processor,
|
|
67
90
|
optionsInput: Partial<AdmonitionOptions> = {},
|
|
68
91
|
): Transformer {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
const keywords = Object.values(options.keywords).map(escapeRegExp).join('|');
|
|
72
|
-
const nestingChar = escapeRegExp(options.tag.slice(0, 1));
|
|
73
|
-
const tag = escapeRegExp(options.tag);
|
|
74
|
-
|
|
75
|
-
// resolve th nesting level of an opening tag
|
|
76
|
-
// ::: -> 0, :::: -> 1, ::::: -> 2 ...
|
|
77
|
-
const nestingLevelRegex = new RegExp(
|
|
78
|
-
`^${tag}(?<nestingLevel>${nestingChar}*)`,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const regex = new RegExp(`${tag}${nestingChar}*(${keywords})(?: *(.*))?\n`);
|
|
82
|
-
const escapeTag = new RegExp(
|
|
83
|
-
escapeRegExp(`\\${options.tag}${options.tag.slice(0, 1)}*`),
|
|
84
|
-
'g',
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// The tokenizer is called on blocks to determine if there is an admonition
|
|
88
|
-
// present and create tags for it
|
|
89
|
-
function blockTokenizer(this: any, eat: any, value: string, silent: boolean) {
|
|
90
|
-
// Stop if no match or match does not start at beginning of line
|
|
91
|
-
const match = regex.exec(value);
|
|
92
|
-
if (!match || match.index !== 0) {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
// If silent return the match
|
|
96
|
-
if (silent) {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const now = eat.now();
|
|
101
|
-
const [opening, keyword, title] = match as string[] as [
|
|
102
|
-
string,
|
|
103
|
-
string,
|
|
104
|
-
string,
|
|
105
|
-
];
|
|
106
|
-
const food = [];
|
|
107
|
-
const content = [];
|
|
108
|
-
// get the nesting level of the opening tag
|
|
109
|
-
const openingLevel =
|
|
110
|
-
nestingLevelRegex.exec(opening)!.groups!.nestingLevel!.length;
|
|
111
|
-
// used as a stack to keep track of nested admonitions
|
|
112
|
-
const nestingLevels: number[] = [openingLevel];
|
|
113
|
-
|
|
114
|
-
let newValue = value;
|
|
115
|
-
// consume lines until a closing tag
|
|
116
|
-
let idx = newValue.indexOf(NEWLINE);
|
|
117
|
-
while (idx !== -1) {
|
|
118
|
-
// grab this line and eat it
|
|
119
|
-
const next = newValue.indexOf(NEWLINE, idx + 1);
|
|
120
|
-
const line =
|
|
121
|
-
next !== -1 ? newValue.slice(idx + 1, next) : newValue.slice(idx + 1);
|
|
122
|
-
food.push(line);
|
|
123
|
-
newValue = newValue.slice(idx + 1);
|
|
124
|
-
const nesting = nestingLevelRegex.exec(line);
|
|
125
|
-
idx = newValue.indexOf(NEWLINE);
|
|
126
|
-
if (!nesting) {
|
|
127
|
-
content.push(line);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
const tagLevel = nesting.groups!.nestingLevel!.length;
|
|
131
|
-
// first level
|
|
132
|
-
if (nestingLevels.length === 0) {
|
|
133
|
-
nestingLevels.push(tagLevel);
|
|
134
|
-
content.push(line);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const currentLevel = nestingLevels[nestingLevels.length - 1]!;
|
|
138
|
-
if (tagLevel < currentLevel) {
|
|
139
|
-
// entering a nested admonition block
|
|
140
|
-
nestingLevels.push(tagLevel);
|
|
141
|
-
} else if (tagLevel === currentLevel) {
|
|
142
|
-
// closing a nested admonition block
|
|
143
|
-
nestingLevels.pop();
|
|
144
|
-
// the closing tag is NOT part of the content
|
|
145
|
-
if (nestingLevels.length === 0) {
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
content.push(line);
|
|
150
|
-
}
|
|
92
|
+
const {keywords} = normalizeAdmonitionOptions(optionsInput);
|
|
151
93
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
94
|
+
return async (root) => {
|
|
95
|
+
visit(root, (node) => {
|
|
96
|
+
if (node.type === 'containerDirective') {
|
|
97
|
+
const directive = node as ContainerDirective;
|
|
98
|
+
const isAdmonition = keywords.includes(directive.name);
|
|
155
99
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
exit();
|
|
100
|
+
if (!isAdmonition) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
160
103
|
|
|
161
|
-
|
|
104
|
+
const {directiveLabel, contentNodes} = parseDirective(directive);
|
|
162
105
|
|
|
163
|
-
|
|
164
|
-
|
|
106
|
+
const textOnlyTitle =
|
|
107
|
+
directive.attributes?.title ??
|
|
108
|
+
(directiveLabel ? getTextOnlyTitle(directiveLabel) : undefined);
|
|
165
109
|
|
|
166
|
-
|
|
167
|
-
type: admonitionNodeType,
|
|
168
|
-
data: {
|
|
169
|
-
// hName/hProperties come from HAST
|
|
110
|
+
// Transform the mdast directive node to a hast admonition node
|
|
170
111
|
// See https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
112
|
+
// TODO in MDX v2 we should transform the whole directive to
|
|
113
|
+
// mdxJsxFlowElement instead of using hast
|
|
114
|
+
directive.data = {
|
|
115
|
+
hName: 'admonition',
|
|
116
|
+
hProperties: {
|
|
117
|
+
...(textOnlyTitle && {title: textOnlyTitle}),
|
|
118
|
+
type: directive.name,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
directive.children = contentNodes;
|
|
122
|
+
|
|
123
|
+
// TODO legacy MDX v1 <mdxAdmonitionTitle> workaround
|
|
124
|
+
// v1: not possible to inject complex JSX elements as props
|
|
125
|
+
// v2: now possible: use a mdxJsxFlowElement element
|
|
126
|
+
if (directiveLabel && !textOnlyTitle) {
|
|
127
|
+
const complexTitleNode = {
|
|
128
|
+
type: 'mdxAdmonitionTitle',
|
|
187
129
|
data: {
|
|
188
130
|
hName: 'mdxAdmonitionTitle',
|
|
189
131
|
hProperties: {},
|
|
190
132
|
},
|
|
191
|
-
children:
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
return add(element);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// add tokenizer to parser after fenced code blocks
|
|
201
|
-
const Parser = this.Parser.prototype;
|
|
202
|
-
Parser.blockTokenizers.admonition = blockTokenizer;
|
|
203
|
-
Parser.blockMethods.splice(
|
|
204
|
-
Parser.blockMethods.indexOf('fencedCode') + 1,
|
|
205
|
-
0,
|
|
206
|
-
'admonition',
|
|
207
|
-
);
|
|
208
|
-
Parser.interruptParagraph.splice(
|
|
209
|
-
Parser.interruptParagraph.indexOf('fencedCode') + 1,
|
|
210
|
-
0,
|
|
211
|
-
['admonition'],
|
|
212
|
-
);
|
|
213
|
-
Parser.interruptList.splice(
|
|
214
|
-
Parser.interruptList.indexOf('fencedCode') + 1,
|
|
215
|
-
0,
|
|
216
|
-
['admonition'],
|
|
217
|
-
);
|
|
218
|
-
Parser.interruptBlockquote.splice(
|
|
219
|
-
Parser.interruptBlockquote.indexOf('fencedCode') + 1,
|
|
220
|
-
0,
|
|
221
|
-
['admonition'],
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
return (root) => {
|
|
225
|
-
// escape everything except admonitionHTML nodes
|
|
226
|
-
visit(
|
|
227
|
-
root,
|
|
228
|
-
(node: unknown): node is Literal =>
|
|
229
|
-
(node as Literal | undefined)?.type !== admonitionNodeType,
|
|
230
|
-
(node: Literal) => {
|
|
231
|
-
if (node.value) {
|
|
232
|
-
node.value = node.value.replace(escapeTag, options.tag);
|
|
133
|
+
children: directiveLabel.children,
|
|
134
|
+
};
|
|
135
|
+
// @ts-expect-error: invented node type
|
|
136
|
+
directive.children.unshift(complexTitleNode);
|
|
233
137
|
}
|
|
234
|
-
}
|
|
235
|
-
);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
236
140
|
};
|
|
237
141
|
};
|
|
238
142
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import visit from 'unist-util-visit';
|
|
9
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
10
|
+
import type {Transformer} from 'unified';
|
|
11
|
+
|
|
12
|
+
// @ts-expect-error: ES support...
|
|
13
|
+
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
|
|
14
|
+
|
|
15
|
+
// Transform <details> to <Details>
|
|
16
|
+
// MDX 2 doesn't allow to substitute html elements with the provider anymore
|
|
17
|
+
export default function plugin(): Transformer {
|
|
18
|
+
return (root) => {
|
|
19
|
+
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
|
|
20
|
+
if (node.name === 'details') {
|
|
21
|
+
node.name = 'Details';
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import visit from 'unist-util-visit';
|
|
9
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
10
|
+
import type {Transformer} from 'unified';
|
|
11
|
+
|
|
12
|
+
// @ts-expect-error: ES support...
|
|
13
|
+
import type {MdxJsxFlowElement} from 'mdast-util-mdx';
|
|
14
|
+
|
|
15
|
+
// Transform <head> to <Head>
|
|
16
|
+
// MDX 2 doesn't allow to substitute html elements with the provider anymore
|
|
17
|
+
export default function plugin(): Transformer {
|
|
18
|
+
return (root) => {
|
|
19
|
+
visit(root, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
|
|
20
|
+
if (node.name === 'head') {
|
|
21
|
+
node.name = 'Head';
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils';
|
|
11
11
|
import visit from 'unist-util-visit';
|
|
12
|
-
|
|
12
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
13
13
|
import type {Transformer} from 'unified';
|
|
14
14
|
import type {Heading, Text} from 'mdast';
|
|
15
15
|
|
|
16
16
|
export default function plugin(): Transformer {
|
|
17
|
-
return (root) => {
|
|
17
|
+
return async (root) => {
|
|
18
|
+
const {toString} = await import('mdast-util-to-string');
|
|
19
|
+
|
|
18
20
|
const slugs = createSlugger();
|
|
19
21
|
visit(root, 'heading', (headingNode: Heading) => {
|
|
20
22
|
const data = headingNode.data ?? (headingNode.data = {});
|
|
@@ -29,7 +31,7 @@ export default function plugin(): Transformer {
|
|
|
29
31
|
const headingTextNodes = headingNode.children.filter(
|
|
30
32
|
({type}) => !['html', 'jsx'].includes(type),
|
|
31
33
|
);
|
|
32
|
-
const heading =
|
|
34
|
+
const heading = toString(
|
|
33
35
|
headingTextNodes.length > 0 ? headingTextNodes : headingNode,
|
|
34
36
|
);
|
|
35
37
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import visit from 'unist-util-visit';
|
|
9
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
10
|
+
import type {Transformer, Processor} from 'unified';
|
|
11
|
+
import type {Code} from 'mdast';
|
|
12
|
+
|
|
13
|
+
// Solution inspired by https://github.com/pomber/docusaurus-mdx-2/blob/main/packages/mdx-loader/src/remark/codeCompat/index.ts
|
|
14
|
+
// TODO after MDX 2 we probably don't need this - remove soon?
|
|
15
|
+
// Only fenced code blocks are swapped by pre/code MDX components
|
|
16
|
+
// Using <pre><code> in JSX shouldn't use our MDX components anymore
|
|
17
|
+
|
|
18
|
+
// To make theme-classic/src/theme/MDXComponents/Pre work
|
|
19
|
+
// we need to fill two properties that mdx v2 doesn't provide anymore
|
|
20
|
+
export default function codeCompatPlugin(this: Processor): Transformer {
|
|
21
|
+
return (root) => {
|
|
22
|
+
visit(root, 'code', (node: Code) => {
|
|
23
|
+
node.data = node.data || {};
|
|
24
|
+
node.data.hProperties = node.data.hProperties || {};
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
(node.data.hProperties as any).metastring = node.meta;
|
|
27
|
+
|
|
28
|
+
// Retrocompatible support for live codeblock metastring
|
|
29
|
+
// Not really the appropriate place to handle that :s
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
(node.data.hProperties as any).live = node.meta
|
|
32
|
+
?.split(' ')
|
|
33
|
+
.includes('live');
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import visit from 'unist-util-visit';
|
|
9
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
9
10
|
import type {Transformer} from 'unified';
|
|
10
11
|
import type {Code} from 'mdast';
|
|
11
12
|
|
|
@@ -17,6 +18,7 @@ export default function plugin(): Transformer {
|
|
|
17
18
|
return (root) => {
|
|
18
19
|
visit(root, 'code', (node: Code, index, parent) => {
|
|
19
20
|
if (node.lang === 'mermaid') {
|
|
21
|
+
// TODO migrate to mdxJsxFlowElement? cf admonitions
|
|
20
22
|
parent!.children.splice(index, 1, {
|
|
21
23
|
type: 'mermaidCodeBlock',
|
|
22
24
|
data: {
|
package/src/remark/toc/index.ts
CHANGED
|
@@ -8,28 +8,42 @@
|
|
|
8
8
|
import {parse, type ParserOptions} from '@babel/parser';
|
|
9
9
|
import traverse from '@babel/traverse';
|
|
10
10
|
import stringifyObject from 'stringify-object';
|
|
11
|
-
import toString from 'mdast-util-to-string';
|
|
12
11
|
import visit from 'unist-util-visit';
|
|
13
12
|
import {toValue} from '../utils';
|
|
14
|
-
|
|
15
13
|
import type {Identifier} from '@babel/types';
|
|
16
|
-
import type {TOCItem} from '../..';
|
|
17
14
|
import type {Node, Parent} from 'unist';
|
|
18
15
|
import type {Heading, Literal} from 'mdast';
|
|
16
|
+
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
|
|
19
17
|
import type {Transformer} from 'unified';
|
|
20
18
|
|
|
19
|
+
// TODO as of April 2023, no way to import/re-export this ESM type easily :/
|
|
20
|
+
// This might change soon, likely after TS 5.2
|
|
21
|
+
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
|
|
22
|
+
// import type {Plugin} from 'unified';
|
|
23
|
+
type Plugin = any; // TODO fix this asap
|
|
24
|
+
|
|
25
|
+
export type TOCItem = {
|
|
26
|
+
readonly value: string;
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly level: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
21
31
|
const parseOptions: ParserOptions = {
|
|
22
32
|
plugins: ['jsx'],
|
|
23
33
|
sourceType: 'module',
|
|
24
34
|
};
|
|
25
35
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const isImport = (child: Node): child is Literal => child.type === 'import';
|
|
36
|
+
const isImport = (child: any): child is Literal =>
|
|
37
|
+
child.type === 'mdxjsEsm' && child.value.startsWith('import');
|
|
29
38
|
const hasImports = (index: number) => index > -1;
|
|
30
|
-
const isExport = (child:
|
|
39
|
+
const isExport = (child: any): child is Literal =>
|
|
40
|
+
child.type === 'mdxjsEsm' && child.value.startsWith('export');
|
|
41
|
+
|
|
42
|
+
interface PluginOptions {
|
|
43
|
+
name?: string;
|
|
44
|
+
}
|
|
31
45
|
|
|
32
|
-
const isTarget = (child: Literal) => {
|
|
46
|
+
const isTarget = (child: Literal, name: string) => {
|
|
33
47
|
let found = false;
|
|
34
48
|
const ast = parse(child.value, parseOptions);
|
|
35
49
|
traverse(ast, {
|
|
@@ -42,24 +56,23 @@ const isTarget = (child: Literal) => {
|
|
|
42
56
|
return found;
|
|
43
57
|
};
|
|
44
58
|
|
|
45
|
-
const getOrCreateExistingTargetIndex = (
|
|
59
|
+
const getOrCreateExistingTargetIndex = async (
|
|
60
|
+
children: Node[],
|
|
61
|
+
name: string,
|
|
62
|
+
) => {
|
|
46
63
|
let importsIndex = -1;
|
|
47
64
|
let targetIndex = -1;
|
|
48
65
|
|
|
49
66
|
children.forEach((child, index) => {
|
|
50
67
|
if (isImport(child)) {
|
|
51
68
|
importsIndex = index;
|
|
52
|
-
} else if (isExport(child) && isTarget(child)) {
|
|
69
|
+
} else if (isExport(child) && isTarget(child, name)) {
|
|
53
70
|
targetIndex = index;
|
|
54
71
|
}
|
|
55
72
|
});
|
|
56
73
|
|
|
57
74
|
if (targetIndex === -1) {
|
|
58
|
-
const target =
|
|
59
|
-
default: false,
|
|
60
|
-
type: 'export',
|
|
61
|
-
value: `export const ${name} = [];`,
|
|
62
|
-
};
|
|
75
|
+
const target = await createExportNode(name, []);
|
|
63
76
|
|
|
64
77
|
targetIndex = hasImports(importsIndex) ? importsIndex + 1 : 0;
|
|
65
78
|
children.splice(targetIndex, 0, target);
|
|
@@ -68,31 +81,72 @@ const getOrCreateExistingTargetIndex = (children: Node[]) => {
|
|
|
68
81
|
return targetIndex;
|
|
69
82
|
};
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
const plugin: Plugin = function plugin(
|
|
85
|
+
options: PluginOptions = {},
|
|
86
|
+
): Transformer {
|
|
87
|
+
const name = options.name || 'toc';
|
|
88
|
+
|
|
89
|
+
return async (root) => {
|
|
90
|
+
const {toString} = await import('mdast-util-to-string');
|
|
73
91
|
const headings: TOCItem[] = [];
|
|
74
92
|
|
|
75
|
-
visit(root, 'heading', (child: Heading
|
|
93
|
+
visit(root, 'heading', (child: Heading) => {
|
|
76
94
|
const value = toString(child);
|
|
77
95
|
|
|
78
|
-
// depth:
|
|
79
|
-
if (
|
|
96
|
+
// depth:1 headings are titles and not included in the TOC
|
|
97
|
+
if (!value || child.depth < 2) {
|
|
80
98
|
return;
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
headings.push({
|
|
84
|
-
value: toValue(child),
|
|
102
|
+
value: toValue(child, toString),
|
|
85
103
|
id: child.data!.id as string,
|
|
86
104
|
level: child.depth,
|
|
87
105
|
});
|
|
88
106
|
});
|
|
89
107
|
const {children} = root as Parent<Literal>;
|
|
90
|
-
const targetIndex = getOrCreateExistingTargetIndex(children);
|
|
108
|
+
const targetIndex = await getOrCreateExistingTargetIndex(children, name);
|
|
91
109
|
|
|
92
|
-
if (headings
|
|
93
|
-
children[targetIndex]
|
|
94
|
-
headings,
|
|
95
|
-
)};`;
|
|
110
|
+
if (headings?.length) {
|
|
111
|
+
children[targetIndex] = await createExportNode(name, headings);
|
|
96
112
|
}
|
|
97
113
|
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export default plugin;
|
|
117
|
+
|
|
118
|
+
async function createExportNode(name: string, object: any) {
|
|
119
|
+
const {valueToEstree} = await import('estree-util-value-to-estree');
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
type: 'mdxjsEsm',
|
|
123
|
+
value: `export const ${name} = ${stringifyObject(object)}`,
|
|
124
|
+
data: {
|
|
125
|
+
estree: {
|
|
126
|
+
type: 'Program',
|
|
127
|
+
body: [
|
|
128
|
+
{
|
|
129
|
+
type: 'ExportNamedDeclaration',
|
|
130
|
+
declaration: {
|
|
131
|
+
type: 'VariableDeclaration',
|
|
132
|
+
declarations: [
|
|
133
|
+
{
|
|
134
|
+
type: 'VariableDeclarator',
|
|
135
|
+
id: {
|
|
136
|
+
type: 'Identifier',
|
|
137
|
+
name,
|
|
138
|
+
},
|
|
139
|
+
init: valueToEstree(object),
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
kind: 'const',
|
|
143
|
+
},
|
|
144
|
+
specifiers: [],
|
|
145
|
+
source: null,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
sourceType: 'module',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
98
152
|
}
|