@mui/internal-markdown 1.0.0
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/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/extractImports.js +8 -0
- package/extractImports.test.js +39 -0
- package/index.d.ts +19 -0
- package/index.js +3 -0
- package/loader.js +415 -0
- package/package.json +35 -0
- package/parseMarkdown.js +481 -0
- package/parseMarkdown.test.js +326 -0
- package/prepareMarkdown.js +259 -0
- package/prepareMarkdown.test.js +417 -0
- package/prism.d.ts +1 -0
- package/prism.js +61 -0
- package/textToHash.js +36 -0
- package/textToHash.test.js +32 -0
package/parseMarkdown.js
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
const { marked } = require('marked');
|
|
2
|
+
const textToHash = require('./textToHash');
|
|
3
|
+
const prism = require('./prism');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Option used by `marked` the library parsing markdown.
|
|
7
|
+
*/
|
|
8
|
+
const markedOptions = {
|
|
9
|
+
gfm: true,
|
|
10
|
+
tables: true,
|
|
11
|
+
breaks: false,
|
|
12
|
+
pedantic: false,
|
|
13
|
+
sanitize: false,
|
|
14
|
+
smartLists: true,
|
|
15
|
+
smartypants: false,
|
|
16
|
+
headerPrefix: false,
|
|
17
|
+
headerIds: false,
|
|
18
|
+
mangle: false,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const headerRegExp = /---[\r\n]([\s\S]*)[\r\n]---/;
|
|
22
|
+
const titleRegExp = /# (.*)[\r\n]/;
|
|
23
|
+
const descriptionRegExp = /<p class="description">(.*?)<\/p>/s;
|
|
24
|
+
const headerKeyValueRegExp = /(.*?):[\r\n]?\s+(\[[^\]]+\]|.*)/g;
|
|
25
|
+
const emptyRegExp = /^\s*$/;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Same as https://github.com/markedjs/marked/blob/master/src/helpers.js
|
|
29
|
+
* Need to duplicate because `marked` does not export `escape` function
|
|
30
|
+
*/
|
|
31
|
+
const escapeTest = /[&<>"']/;
|
|
32
|
+
const escapeReplace = /[&<>"']/g;
|
|
33
|
+
const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
|
|
34
|
+
const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
|
|
35
|
+
const escapeReplacements = {
|
|
36
|
+
'&': '&',
|
|
37
|
+
'<': '<',
|
|
38
|
+
'>': '>',
|
|
39
|
+
'"': '"',
|
|
40
|
+
"'": ''',
|
|
41
|
+
};
|
|
42
|
+
const getEscapeReplacement = (ch) => escapeReplacements[ch];
|
|
43
|
+
function escape(html, encode) {
|
|
44
|
+
if (encode) {
|
|
45
|
+
if (escapeTest.test(html)) {
|
|
46
|
+
return html.replace(escapeReplace, getEscapeReplacement);
|
|
47
|
+
}
|
|
48
|
+
} else if (escapeTestNoEncode.test(html)) {
|
|
49
|
+
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return html;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function checkUrlHealth(href, linkText, context) {
|
|
56
|
+
const url = new URL(href, 'https://mui.com/');
|
|
57
|
+
|
|
58
|
+
if (/\/{2,}$/.test(url.pathname)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
[
|
|
61
|
+
'docs-infra: Duplicated trailing slashes. The following link:',
|
|
62
|
+
`[${linkText}](${href}) in ${context.location} has duplicated trailing slashes, please only add one.`,
|
|
63
|
+
'',
|
|
64
|
+
'See https://ahrefs.com/blog/trailing-slash/ for more details.',
|
|
65
|
+
'',
|
|
66
|
+
].join('\n'),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// External links to MUI, ignore
|
|
71
|
+
if (url.host !== 'mui.com') {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Break for links like:
|
|
77
|
+
* /material-ui/customization/theming
|
|
78
|
+
*
|
|
79
|
+
* It needs to be:
|
|
80
|
+
* /material-ui/customization/theming/
|
|
81
|
+
*/
|
|
82
|
+
if (url.pathname[url.pathname.length - 1] !== '/') {
|
|
83
|
+
throw new Error(
|
|
84
|
+
[
|
|
85
|
+
'docs-infra: Missing trailing slash. The following link:',
|
|
86
|
+
`[${linkText}](${href}) in ${context.location} is missing a trailing slash, please add it.`,
|
|
87
|
+
'',
|
|
88
|
+
'See https://ahrefs.com/blog/trailing-slash/ for more details.',
|
|
89
|
+
'',
|
|
90
|
+
].join('\n'),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Relative links
|
|
95
|
+
if (href[0] !== '#' && !(href.startsWith('https://') || href.startsWith('http://'))) {
|
|
96
|
+
/**
|
|
97
|
+
* Break for links like:
|
|
98
|
+
* material-ui/customization/theming/
|
|
99
|
+
*
|
|
100
|
+
* It needs to be:
|
|
101
|
+
* /material-ui/customization/theming/
|
|
102
|
+
*/
|
|
103
|
+
if (href[0] !== '/') {
|
|
104
|
+
throw new Error(
|
|
105
|
+
[
|
|
106
|
+
'docs-infra: Missing leading slash. The following link:',
|
|
107
|
+
`[${linkText}](${href}) in ${context.location} is missing a leading slash, please add it.`,
|
|
108
|
+
'',
|
|
109
|
+
].join('\n'),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extract information from the top of the markdown.
|
|
117
|
+
* For instance, the following input:
|
|
118
|
+
*
|
|
119
|
+
* ---
|
|
120
|
+
* title: Backdrop React Component
|
|
121
|
+
* components: Backdrop
|
|
122
|
+
* ---
|
|
123
|
+
*
|
|
124
|
+
* # Backdrop
|
|
125
|
+
*
|
|
126
|
+
* should output:
|
|
127
|
+
* { title: 'Backdrop React Component', components: ['Backdrop'] }
|
|
128
|
+
*/
|
|
129
|
+
function getHeaders(markdown) {
|
|
130
|
+
let header = markdown.match(headerRegExp);
|
|
131
|
+
|
|
132
|
+
if (!header) {
|
|
133
|
+
return {
|
|
134
|
+
components: [],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
header = header[1];
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
let regexMatches;
|
|
142
|
+
const headers = {};
|
|
143
|
+
|
|
144
|
+
// eslint-disable-next-line no-cond-assign
|
|
145
|
+
while ((regexMatches = headerKeyValueRegExp.exec(header)) !== null) {
|
|
146
|
+
const key = regexMatches[1];
|
|
147
|
+
let value = regexMatches[2].replace(/(.*)/, '$1');
|
|
148
|
+
if (value[0] === '[') {
|
|
149
|
+
// Need double quotes to JSON parse.
|
|
150
|
+
value = value.replace(/'/g, '"');
|
|
151
|
+
// Remove the comma after the last value e.g. ["foo", "bar",] -> ["foo", "bar"].
|
|
152
|
+
value = value.replace(/,\s+\]$/g, ']');
|
|
153
|
+
headers[key] = JSON.parse(value);
|
|
154
|
+
} else {
|
|
155
|
+
// Remove trailing single quote yml escaping.
|
|
156
|
+
headers[key] = value.replace(/^'|'$/g, '');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (headers.components) {
|
|
161
|
+
headers.components = headers.components
|
|
162
|
+
.split(',')
|
|
163
|
+
.map((x) => x.trim())
|
|
164
|
+
.sort();
|
|
165
|
+
} else {
|
|
166
|
+
headers.components = [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (headers.hooks) {
|
|
170
|
+
headers.hooks = headers.hooks
|
|
171
|
+
.split(',')
|
|
172
|
+
.map((x) => x.trim())
|
|
173
|
+
.sort();
|
|
174
|
+
} else {
|
|
175
|
+
headers.hooks = [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return headers;
|
|
179
|
+
} catch (err) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`docs-infra: ${err.message} in getHeader(markdown) with markdown: \n\n${header}\n`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getContents(markdown) {
|
|
187
|
+
const rep = markdown
|
|
188
|
+
.replace(headerRegExp, '') // Remove header information
|
|
189
|
+
.split(/^{{("(?:demo|component)":.*)}}$/gm) // Split markdown into an array, separating demos
|
|
190
|
+
.flatMap((text) => text.split(/^(<codeblock.*?<\/codeblock>)$/gmsu))
|
|
191
|
+
.filter((content) => !emptyRegExp.test(content)); // Remove empty lines
|
|
192
|
+
return rep;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getTitle(markdown) {
|
|
196
|
+
const matches = markdown.match(titleRegExp);
|
|
197
|
+
|
|
198
|
+
if (matches === null) {
|
|
199
|
+
return '';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return matches[1].replace(/`/g, '');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getDescription(markdown) {
|
|
206
|
+
const matches = markdown.match(descriptionRegExp);
|
|
207
|
+
if (matches === null) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return matches[1].trim().replace(/`/g, '');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getCodeblock(content) {
|
|
215
|
+
if (content.startsWith('<codeblock')) {
|
|
216
|
+
const storageKey = content.match(/^<codeblock [^>]*storageKey=["|'](\S*)["|'].*>/m)?.[1];
|
|
217
|
+
const blocks = [...content.matchAll(/^```(\S*) (\S*)\n(.*?)\n```/gmsu)].map(
|
|
218
|
+
([, language, tab, code]) => ({ language, tab, code }),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const blocksData = blocks.filter(
|
|
222
|
+
(block) => block.tab !== undefined && !emptyRegExp.test(block.code),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
type: 'codeblock',
|
|
227
|
+
data: blocksData,
|
|
228
|
+
storageKey,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {string} markdown
|
|
236
|
+
*/
|
|
237
|
+
function renderMarkdown(markdown) {
|
|
238
|
+
// Check if the markdown contains an inline list. Unordered lists are block elements and cannot be parsed inline.
|
|
239
|
+
if (/[-*+] `([A-Za-z]+)`/g.test(markdown)) {
|
|
240
|
+
return marked.parse(markdown, markedOptions);
|
|
241
|
+
}
|
|
242
|
+
// Two new lines result in a newline in the table.
|
|
243
|
+
// All other new lines must be eliminated to prevent markdown mayhem.
|
|
244
|
+
return marked
|
|
245
|
+
.parseInline(markdown, markedOptions)
|
|
246
|
+
.replace(/(\r?\n){2}/g, '<br>')
|
|
247
|
+
.replace(/\r?\n/g, ' ');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Help rank mui.com on component searches first.
|
|
251
|
+
const noSEOadvantage = [
|
|
252
|
+
'https://m2.material.io/',
|
|
253
|
+
'https://m3.material.io/',
|
|
254
|
+
'https://material.io/',
|
|
255
|
+
'https://getbootstrap.com/',
|
|
256
|
+
'https://icons.getbootstrap.com/',
|
|
257
|
+
'https://pictogrammers.com/',
|
|
258
|
+
'https://www.w3.org/',
|
|
259
|
+
'https://tailwindcss.com/',
|
|
260
|
+
'https://heroicons.com/',
|
|
261
|
+
'https://react-icons.github.io/',
|
|
262
|
+
'https://fontawesome.com/',
|
|
263
|
+
'https://www.radix-ui.com/',
|
|
264
|
+
'https://react-spectrum.adobe.com/',
|
|
265
|
+
'https://headlessui.com/',
|
|
266
|
+
'https://refine.dev/',
|
|
267
|
+
'https://scaffoldhub.io/',
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Creates a function that MUST be used to render non-inline markdown.
|
|
272
|
+
* It keeps track of a table of contents and hashes of its items.
|
|
273
|
+
* This is important to create anchors that are invariant between languages.
|
|
274
|
+
*
|
|
275
|
+
* @typedef {object} TableOfContentsEntry
|
|
276
|
+
* @property {TableOfContentsEntry[]} children
|
|
277
|
+
* @property {string} hash
|
|
278
|
+
* @property {number} level
|
|
279
|
+
* @property {string} text
|
|
280
|
+
* @param {object} context
|
|
281
|
+
* @param {Record<string, string>} context.headingHashes - WILL BE MUTATED
|
|
282
|
+
* @param {TableOfContentsEntry[]} context.toc - WILL BE MUTATED
|
|
283
|
+
* @param {string} context.userLanguage
|
|
284
|
+
* @param {object} context.options
|
|
285
|
+
*/
|
|
286
|
+
function createRender(context) {
|
|
287
|
+
const { headingHashes, toc, userLanguage, options } = context;
|
|
288
|
+
const headingHashesFallbackTranslated = {};
|
|
289
|
+
let headingIndex = -1;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @param {string} markdown
|
|
293
|
+
*/
|
|
294
|
+
function render(markdown) {
|
|
295
|
+
const renderer = new marked.Renderer();
|
|
296
|
+
renderer.heading = (headingHtml, level) => {
|
|
297
|
+
// Main title, no need for an anchor.
|
|
298
|
+
// It adds noises to the URL.
|
|
299
|
+
//
|
|
300
|
+
// Small title, no need for an anchor.
|
|
301
|
+
// It reduces the risk of duplicated id and it's fewer elements in the DOM.
|
|
302
|
+
if (level === 1 || level >= 4) {
|
|
303
|
+
return `<h${level}>${headingHtml}</h${level}>`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Remove links to avoid nested links in the TOCs
|
|
307
|
+
let headingText = headingHtml.replace(/<a\b[^>]*>/gi, '').replace(/<\/a>/gi, '');
|
|
308
|
+
// Remove `code` tags
|
|
309
|
+
headingText = headingText.replace(/<code\b[^>]*>/gi, '').replace(/<\/code>/gi, '');
|
|
310
|
+
|
|
311
|
+
// Standardizes the hash from the default location (en) to different locations
|
|
312
|
+
// Need english.md file parsed first
|
|
313
|
+
let hash;
|
|
314
|
+
if (userLanguage === 'en') {
|
|
315
|
+
hash = textToHash(headingText, headingHashes);
|
|
316
|
+
} else {
|
|
317
|
+
headingIndex += 1;
|
|
318
|
+
hash = Object.keys(headingHashes)[headingIndex];
|
|
319
|
+
if (!hash) {
|
|
320
|
+
hash = textToHash(headingText, headingHashesFallbackTranslated);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// enable splitting of long words from function name + first arg name
|
|
325
|
+
// Closing parens are less interesting since this would only allow breaking one character earlier.
|
|
326
|
+
// Applying the same mechanism would also allow breaking of non-function signatures like "Community help (free)".
|
|
327
|
+
// To detect that we enabled breaking of open/closing parens we'd need a context-sensitive parser.
|
|
328
|
+
const displayText = headingText.replace(/([^\s]\()/g, '$1​');
|
|
329
|
+
|
|
330
|
+
// create a nested structure with 2 levels starting with level 2 e.g.
|
|
331
|
+
// [{...level2, children: [level3, level3, level3]}, level2]
|
|
332
|
+
if (level === 2) {
|
|
333
|
+
toc.push({
|
|
334
|
+
text: displayText,
|
|
335
|
+
level,
|
|
336
|
+
hash,
|
|
337
|
+
children: [],
|
|
338
|
+
});
|
|
339
|
+
} else if (level === 3) {
|
|
340
|
+
if (!toc[toc.length - 1]) {
|
|
341
|
+
throw new Error(`docs-infra: Missing parent level for: ${headingText}\n`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
toc[toc.length - 1].children.push({
|
|
345
|
+
text: displayText,
|
|
346
|
+
level,
|
|
347
|
+
hash,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return [
|
|
352
|
+
`<h${level} id="${hash}">`,
|
|
353
|
+
headingHtml,
|
|
354
|
+
`<a aria-labelledby="${hash}" class="anchor-link" href="#${hash}" tabindex="-1">`,
|
|
355
|
+
'<svg><use xlink:href="#anchor-link-icon" /></svg>',
|
|
356
|
+
'</a>',
|
|
357
|
+
`<button title="Post a comment" class="comment-link" data-feedback-hash="${hash}">`,
|
|
358
|
+
'<svg><use xlink:href="#comment-link-icon" /></svg>',
|
|
359
|
+
`</button>`,
|
|
360
|
+
`</h${level}>`,
|
|
361
|
+
].join('');
|
|
362
|
+
};
|
|
363
|
+
renderer.link = (href, linkTitle, linkText) => {
|
|
364
|
+
let more = '';
|
|
365
|
+
|
|
366
|
+
if (linkTitle) {
|
|
367
|
+
more += ` title="${linkTitle}"`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (noSEOadvantage.some((domain) => href.indexOf(domain) !== -1)) {
|
|
371
|
+
more = ' target="_blank" rel="noopener nofollow"';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
let finalHref = href;
|
|
375
|
+
|
|
376
|
+
checkUrlHealth(href, linkText, context);
|
|
377
|
+
|
|
378
|
+
if (userLanguage !== 'en' && href.indexOf('/') === 0 && !options.ignoreLanguagePages(href)) {
|
|
379
|
+
finalHref = `/${userLanguage}${href}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// This logic turns link like:
|
|
383
|
+
// https://github.com/mui/material-ui/blob/-/packages/mui-joy/src/styles/components.d.ts
|
|
384
|
+
// into a permalink:
|
|
385
|
+
// https://github.com/mui/material-ui/blob/v5.11.15/packages/mui-joy/src/styles/components.d.ts
|
|
386
|
+
if (finalHref.startsWith(`${options.env.SOURCE_CODE_REPO}/blob/-/`)) {
|
|
387
|
+
finalHref = finalHref.replace(
|
|
388
|
+
`${options.env.SOURCE_CODE_REPO}/blob/-/`,
|
|
389
|
+
`${options.env.SOURCE_CODE_REPO}/blob/v${options.env.LIB_VERSION}/`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return `<a href="${finalHref}"${more}>${linkText}</a>`;
|
|
394
|
+
};
|
|
395
|
+
renderer.code = (code, infostring, escaped) => {
|
|
396
|
+
// https://github.com/markedjs/marked/blob/30e90e5175700890e6feb1836c57b9404c854466/src/Renderer.js#L15
|
|
397
|
+
const lang = (infostring || '').match(/\S*/)[0];
|
|
398
|
+
const out = prism(code, lang);
|
|
399
|
+
if (out != null && out !== code) {
|
|
400
|
+
escaped = true;
|
|
401
|
+
code = out;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
code = `${code.replace(/\n$/, '')}\n`;
|
|
405
|
+
|
|
406
|
+
if (!lang) {
|
|
407
|
+
return `<pre><code>${escaped ? code : escape(code, true)}</code></pre>\n`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return `<div class="MuiCode-root"><pre><code class="language-${escape(lang, true)}">${
|
|
411
|
+
escaped ? code : escape(code, true)
|
|
412
|
+
}</code></pre>${[
|
|
413
|
+
'<button data-ga-event-category="code" data-ga-event-action="copy-click" aria-label="Copy the code" class="MuiCode-copy">',
|
|
414
|
+
'<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ContentCopyRoundedIcon">',
|
|
415
|
+
'<use class="MuiCode-copy-icon" xlink:href="#copy-icon" />',
|
|
416
|
+
'<use class="MuiCode-copied-icon" xlink:href="#copied-icon" />',
|
|
417
|
+
'</svg>',
|
|
418
|
+
'<span class="MuiCode-copyKeypress"><span>(or</span> $keyC<span>)</span></span></button></div>',
|
|
419
|
+
].join('')}\n`;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
marked.use({
|
|
423
|
+
extensions: [
|
|
424
|
+
{
|
|
425
|
+
name: 'callout',
|
|
426
|
+
level: 'block',
|
|
427
|
+
start(src) {
|
|
428
|
+
const match = src.match(/:::/);
|
|
429
|
+
return match ? match.index : undefined;
|
|
430
|
+
},
|
|
431
|
+
tokenizer(src) {
|
|
432
|
+
const rule =
|
|
433
|
+
/^ {0,3}(:{3,}(?=[^:\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~:]* *(?=\n|$)|$)/;
|
|
434
|
+
const match = rule.exec(src);
|
|
435
|
+
if (match) {
|
|
436
|
+
const token = {
|
|
437
|
+
type: 'callout',
|
|
438
|
+
raw: match[0],
|
|
439
|
+
text: match[3].trim(),
|
|
440
|
+
severity: match[2],
|
|
441
|
+
tokens: [],
|
|
442
|
+
};
|
|
443
|
+
this.lexer.blockTokens(token.text, token.tokens);
|
|
444
|
+
return token;
|
|
445
|
+
}
|
|
446
|
+
return undefined;
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
renderer(token) {
|
|
450
|
+
return `<aside class="MuiCallout-root MuiCallout-${token.severity}">
|
|
451
|
+
${
|
|
452
|
+
['info', 'success', 'warning', 'error'].includes(token.severity)
|
|
453
|
+
? [
|
|
454
|
+
'<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ContentCopyRoundedIcon">',
|
|
455
|
+
`<use class="MuiCode-copied-icon" xlink:href="#${token.severity}-icon" />`,
|
|
456
|
+
'</svg>',
|
|
457
|
+
].join('\n')
|
|
458
|
+
: ''
|
|
459
|
+
}
|
|
460
|
+
<div class="MuiCallout-content">
|
|
461
|
+
${this.parser.parse(token.tokens)}\n</div></aside>`;
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
],
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return marked(markdown, { ...markedOptions, renderer });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return render;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
module.exports = {
|
|
474
|
+
createRender,
|
|
475
|
+
getContents,
|
|
476
|
+
getDescription,
|
|
477
|
+
getCodeblock,
|
|
478
|
+
getHeaders,
|
|
479
|
+
getTitle,
|
|
480
|
+
renderMarkdown,
|
|
481
|
+
};
|