@docusaurus/utils 2.0.0-beta.12faed89d → 2.0.0-beta.13

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.
Files changed (50) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/constants.d.ts +19 -0
  3. package/lib/constants.js +26 -0
  4. package/lib/globUtils.d.ts +11 -0
  5. package/lib/globUtils.js +47 -0
  6. package/lib/hashUtils.js +4 -4
  7. package/lib/index.d.ts +10 -6
  8. package/lib/index.js +39 -92
  9. package/lib/markdownLinks.js +19 -6
  10. package/lib/markdownParser.js +14 -6
  11. package/lib/mdxUtils.d.ts +16 -0
  12. package/lib/mdxUtils.js +30 -0
  13. package/lib/normalizeUrl.d.ts +7 -0
  14. package/lib/normalizeUrl.js +66 -0
  15. package/lib/pathUtils.js +3 -5
  16. package/lib/slugger.d.ts +13 -0
  17. package/lib/slugger.js +18 -0
  18. package/lib/tags.d.ts +18 -0
  19. package/lib/tags.js +72 -0
  20. package/lib/webpackUtils.d.ts +29 -0
  21. package/lib/webpackUtils.js +109 -0
  22. package/package.json +23 -7
  23. package/src/__tests__/globUtils.test.ts +109 -0
  24. package/src/__tests__/index.test.ts +6 -110
  25. package/src/__tests__/markdownParser.test.ts +15 -2
  26. package/src/__tests__/mdxUtils.test.ts +133 -0
  27. package/src/__tests__/normalizeUrl.test.ts +117 -0
  28. package/src/__tests__/pathUtils.test.ts +4 -2
  29. package/src/__tests__/slugger.test.ts +27 -0
  30. package/src/__tests__/tags.test.ts +183 -0
  31. package/src/__tests__/webpackUtils.test.ts +33 -0
  32. package/src/constants.ts +38 -0
  33. package/src/deps.d.ts +14 -0
  34. package/src/globUtils.ts +63 -0
  35. package/src/index.ts +21 -91
  36. package/src/markdownLinks.ts +19 -8
  37. package/src/markdownParser.ts +18 -11
  38. package/src/mdxUtils.ts +32 -0
  39. package/src/normalizeUrl.ts +80 -0
  40. package/src/pathUtils.ts +2 -3
  41. package/src/slugger.ts +24 -0
  42. package/src/tags.ts +100 -0
  43. package/src/webpackUtils.ts +144 -0
  44. package/lib/codeTranslationsUtils.d.ts +0 -11
  45. package/lib/codeTranslationsUtils.js +0 -50
  46. package/src/__tests__/__fixtures__/defaultCodeTranslations/en.json +0 -4
  47. package/src/__tests__/__fixtures__/defaultCodeTranslations/fr-FR.json +0 -5
  48. package/src/__tests__/__fixtures__/defaultCodeTranslations/fr.json +0 -4
  49. package/src/__tests__/codeTranslationsUtils.test.ts +0 -112
  50. package/src/codeTranslationsUtils.ts +0 -56
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getFileLoaderUtils = void 0;
10
+ const tslib_1 = require("tslib");
11
+ const path_1 = (0, tslib_1.__importDefault)(require("path"));
12
+ const posixPath_1 = require("./posixPath");
13
+ const constants_1 = require("./constants");
14
+ // Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
15
+ function getFileLoaderUtils() {
16
+ // files/images < urlLoaderLimit will be inlined as base64 strings directly in the html
17
+ const urlLoaderLimit = constants_1.WEBPACK_URL_LOADER_LIMIT;
18
+ // defines the path/pattern of the assets handled by webpack
19
+ const fileLoaderFileName = (folder) => `${constants_1.OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[hash].[ext]`;
20
+ const loaders = {
21
+ file: (options) => ({
22
+ loader: require.resolve(`file-loader`),
23
+ options: {
24
+ name: fileLoaderFileName(options.folder),
25
+ },
26
+ }),
27
+ url: (options) => ({
28
+ loader: require.resolve('url-loader'),
29
+ options: {
30
+ limit: urlLoaderLimit,
31
+ name: fileLoaderFileName(options.folder),
32
+ fallback: require.resolve('file-loader'),
33
+ },
34
+ }),
35
+ // TODO find a better solution to avoid conflicts with the ideal-image plugin
36
+ // TODO this may require a little breaking change for ideal-image users?
37
+ // Maybe with the ideal image plugin, all md images should be "ideal"?
38
+ // This is used to force url-loader+file-loader on markdown images
39
+ // https://webpack.js.org/concepts/loaders/#inline
40
+ inlineMarkdownImageFileLoader: `!${(0, posixPath_1.posixPath)(require.resolve('url-loader'))}?limit=${urlLoaderLimit}&name=${fileLoaderFileName('images')}&fallback=${(0, posixPath_1.posixPath)(require.resolve('file-loader'))}!`,
41
+ inlineMarkdownLinkFileLoader: `!${(0, posixPath_1.posixPath)(require.resolve('file-loader'))}?name=${fileLoaderFileName('files')}!`,
42
+ };
43
+ const rules = {
44
+ /**
45
+ * Loads image assets, inlines images via a data URI if they are below
46
+ * the size threshold
47
+ */
48
+ images: () => ({
49
+ use: [loaders.url({ folder: 'images' })],
50
+ test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
51
+ }),
52
+ fonts: () => ({
53
+ use: [loaders.url({ folder: 'fonts' })],
54
+ test: /\.(woff|woff2|eot|ttf|otf)$/,
55
+ }),
56
+ /**
57
+ * Loads audio and video and inlines them via a data URI if they are below
58
+ * the size threshold
59
+ */
60
+ media: () => ({
61
+ use: [loaders.url({ folder: 'medias' })],
62
+ test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
63
+ }),
64
+ svg: () => ({
65
+ test: /\.svg?$/,
66
+ oneOf: [
67
+ {
68
+ use: [
69
+ {
70
+ loader: require.resolve('@svgr/webpack'),
71
+ options: {
72
+ prettier: false,
73
+ svgo: true,
74
+ svgoConfig: {
75
+ plugins: [
76
+ {
77
+ name: 'preset-default',
78
+ params: {
79
+ overrides: {
80
+ removeViewBox: false,
81
+ },
82
+ },
83
+ },
84
+ ],
85
+ },
86
+ titleProp: true,
87
+ ref: ![path_1.default],
88
+ },
89
+ },
90
+ ],
91
+ // We don't want to use SVGR loader for non-React source code
92
+ // ie we don't want to use SVGR for CSS files...
93
+ issuer: {
94
+ and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
95
+ },
96
+ },
97
+ {
98
+ use: [loaders.url({ folder: 'images' })],
99
+ },
100
+ ],
101
+ }),
102
+ otherAssets: () => ({
103
+ use: [loaders.file({ folder: 'files' })],
104
+ test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
105
+ }),
106
+ };
107
+ return { loaders, rules };
108
+ }
109
+ exports.getFileLoaderUtils = getFileLoaderUtils;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/utils",
3
- "version": "2.0.0-beta.12faed89d",
3
+ "version": "2.0.0-beta.13",
4
4
  "description": "Node utility functions for Docusaurus packages.",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -18,22 +18,38 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@docusaurus/types": "2.0.0-beta.12faed89d",
22
- "@types/github-slugger": "^1.3.0",
23
- "chalk": "^4.1.1",
21
+ "@mdx-js/runtime": "^1.6.22",
22
+ "@svgr/webpack": "^6.0.0",
23
+ "chalk": "^4.1.2",
24
24
  "escape-string-regexp": "^4.0.0",
25
+ "file-loader": "^6.2.0",
25
26
  "fs-extra": "^10.0.0",
27
+ "github-slugger": "^1.4.0",
28
+ "globby": "^11.0.4",
26
29
  "gray-matter": "^4.0.3",
27
30
  "lodash": "^4.17.20",
31
+ "micromatch": "^4.0.4",
32
+ "remark-mdx-remove-exports": "^1.6.22",
33
+ "remark-mdx-remove-imports": "^1.6.22",
28
34
  "resolve-pathname": "^3.0.0",
29
- "tslib": "^2.2.0"
35
+ "tslib": "^2.3.1",
36
+ "url-loader": "^4.1.1"
30
37
  },
31
38
  "engines": {
32
- "node": ">=12.13.0"
39
+ "node": ">=14"
33
40
  },
34
41
  "devDependencies": {
42
+ "@docusaurus/types": "2.0.0-beta.13",
35
43
  "@types/dedent": "^0.7.0",
44
+ "@types/github-slugger": "^1.3.0",
45
+ "@types/micromatch": "^4.0.2",
46
+ "@types/react-dom": "^17.0.1",
36
47
  "dedent": "^0.7.0"
37
48
  },
38
- "gitHead": "1430dbacfbe9da59a17ce395ed29bdbddaf90049"
49
+ "peerDependencies": {
50
+ "react": "*",
51
+ "react-dom": "*",
52
+ "webpack": "5.x"
53
+ },
54
+ "gitHead": "b6ca12aedf10a10c2375f4f8b7e7380cfd7c7202"
39
55
  }
@@ -0,0 +1,109 @@
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 {
9
+ GlobExcludeDefault,
10
+ createMatcher,
11
+ createAbsoluteFilePathMatcher,
12
+ } from '../globUtils';
13
+
14
+ describe('createMatcher', () => {
15
+ const matcher = createMatcher(GlobExcludeDefault);
16
+
17
+ test('match default exclude MD/MDX partials correctly', () => {
18
+ expect(matcher('doc.md')).toEqual(false);
19
+ expect(matcher('category/doc.md')).toEqual(false);
20
+ expect(matcher('category/subcategory/doc.md')).toEqual(false);
21
+ //
22
+ expect(matcher('doc.mdx')).toEqual(false);
23
+ expect(matcher('category/doc.mdx')).toEqual(false);
24
+ expect(matcher('category/subcategory/doc.mdx')).toEqual(false);
25
+ //
26
+ expect(matcher('_doc.md')).toEqual(true);
27
+ expect(matcher('category/_doc.md')).toEqual(true);
28
+ expect(matcher('category/subcategory/_doc.md')).toEqual(true);
29
+ expect(matcher('_category/doc.md')).toEqual(true);
30
+ expect(matcher('_category/subcategory/doc.md')).toEqual(true);
31
+ expect(matcher('category/_subcategory/doc.md')).toEqual(true);
32
+ });
33
+
34
+ test('match default exclude tests correctly', () => {
35
+ expect(matcher('xyz.js')).toEqual(false);
36
+ expect(matcher('xyz.ts')).toEqual(false);
37
+ expect(matcher('xyz.jsx')).toEqual(false);
38
+ expect(matcher('xyz.tsx')).toEqual(false);
39
+ expect(matcher('folder/xyz.js')).toEqual(false);
40
+ expect(matcher('folder/xyz.ts')).toEqual(false);
41
+ expect(matcher('folder/xyz.jsx')).toEqual(false);
42
+ expect(matcher('folder/xyz.tsx')).toEqual(false);
43
+ //
44
+ expect(matcher('xyz.test.js')).toEqual(true);
45
+ expect(matcher('xyz.test.ts')).toEqual(true);
46
+ expect(matcher('xyz.test.jsx')).toEqual(true);
47
+ expect(matcher('xyz.test.tsx')).toEqual(true);
48
+ expect(matcher('folder/xyz.test.js')).toEqual(true);
49
+ expect(matcher('folder/xyz.test.ts')).toEqual(true);
50
+ expect(matcher('folder/xyz.test.jsx')).toEqual(true);
51
+ expect(matcher('folder/xyz.test.tsx')).toEqual(true);
52
+ expect(matcher('folder/subfolder/xyz.test.js')).toEqual(true);
53
+ expect(matcher('folder/subfolder/xyz.test.ts')).toEqual(true);
54
+ expect(matcher('folder/subfolder/xyz.test.jsx')).toEqual(true);
55
+ expect(matcher('folder/subfolder/xyz.test.tsx')).toEqual(true);
56
+ //
57
+ expect(matcher('__tests__/subfolder/xyz.js')).toEqual(true);
58
+ expect(matcher('__tests__/subfolder/xyz.ts')).toEqual(true);
59
+ expect(matcher('__tests__/subfolder/xyz.jsx')).toEqual(true);
60
+ expect(matcher('__tests__/subfolder/xyz.tsx')).toEqual(true);
61
+ expect(matcher('folder/__tests__/xyz.js')).toEqual(true);
62
+ expect(matcher('folder/__tests__/xyz.ts')).toEqual(true);
63
+ expect(matcher('folder/__tests__/xyz.jsx')).toEqual(true);
64
+ expect(matcher('folder/__tests__/xyz.tsx')).toEqual(true);
65
+ });
66
+ });
67
+
68
+ describe('createAbsoluteFilePathMatcher', () => {
69
+ const rootFolders = ['/_root/docs', '/root/_docs/', '/__test__/website/src'];
70
+
71
+ const matcher = createAbsoluteFilePathMatcher(
72
+ GlobExcludeDefault,
73
+ rootFolders,
74
+ );
75
+
76
+ test('match default exclude MD/MDX partials correctly', () => {
77
+ expect(matcher('/_root/docs/myDoc.md')).toEqual(false);
78
+ expect(matcher('/_root/docs/myDoc.mdx')).toEqual(false);
79
+ expect(matcher('/root/_docs/myDoc.md')).toEqual(false);
80
+ expect(matcher('/root/_docs/myDoc.mdx')).toEqual(false);
81
+ expect(matcher('/_root/docs/category/myDoc.md')).toEqual(false);
82
+ expect(matcher('/_root/docs/category/myDoc.mdx')).toEqual(false);
83
+ expect(matcher('/root/_docs/category/myDoc.md')).toEqual(false);
84
+ expect(matcher('/root/_docs/category/myDoc.mdx')).toEqual(false);
85
+ //
86
+ expect(matcher('/_root/docs/_myDoc.md')).toEqual(true);
87
+ expect(matcher('/_root/docs/_myDoc.mdx')).toEqual(true);
88
+ expect(matcher('/root/_docs/_myDoc.md')).toEqual(true);
89
+ expect(matcher('/root/_docs/_myDoc.mdx')).toEqual(true);
90
+ expect(matcher('/_root/docs/_category/myDoc.md')).toEqual(true);
91
+ expect(matcher('/_root/docs/_category/myDoc.mdx')).toEqual(true);
92
+ expect(matcher('/root/_docs/_category/myDoc.md')).toEqual(true);
93
+ expect(matcher('/root/_docs/_category/myDoc.mdx')).toEqual(true);
94
+ });
95
+
96
+ test('match default exclude tests correctly', () => {
97
+ expect(matcher('/__test__/website/src/xyz.js')).toEqual(false);
98
+ expect(matcher('/__test__/website/src/__test__/xyz.js')).toEqual(true);
99
+ expect(matcher('/__test__/website/src/xyz.test.js')).toEqual(true);
100
+ });
101
+
102
+ test('throw if file is not contained in any root doc', () => {
103
+ expect(() =>
104
+ matcher('/bad/path/myDoc.md'),
105
+ ).toThrowErrorMatchingInlineSnapshot(
106
+ `"createAbsoluteFilePathMatcher unexpected error, absoluteFilePath=/bad/path/myDoc.md was not contained in any of the root folders [\\"/_root/docs\\",\\"/root/_docs/\\",\\"/__test__/website/src\\"]"`,
107
+ );
108
+ });
109
+ });
@@ -12,7 +12,6 @@ import {
12
12
  genChunkName,
13
13
  idx,
14
14
  getSubFolder,
15
- normalizeUrl,
16
15
  posixPath,
17
16
  objectWithKeySorted,
18
17
  aliasedSitePath,
@@ -218,113 +217,6 @@ describe('load utils', () => {
218
217
  expect(getSubFolder(testE, 'docs')).toBeNull();
219
218
  });
220
219
 
221
- test('normalizeUrl', () => {
222
- const asserts = [
223
- {
224
- input: ['/', ''],
225
- output: '/',
226
- },
227
- {
228
- input: ['', '/'],
229
- output: '/',
230
- },
231
- {
232
- input: ['/'],
233
- output: '/',
234
- },
235
- {
236
- input: [''],
237
- output: '',
238
- },
239
- {
240
- input: ['/', '/'],
241
- output: '/',
242
- },
243
- {
244
- input: ['/', 'docs'],
245
- output: '/docs',
246
- },
247
- {
248
- input: ['/', 'docs', 'en', 'next', 'blog'],
249
- output: '/docs/en/next/blog',
250
- },
251
- {
252
- input: ['/test/', '/docs', 'ro', 'doc1'],
253
- output: '/test/docs/ro/doc1',
254
- },
255
- {
256
- input: ['/test/', '/', 'ro', 'doc1'],
257
- output: '/test/ro/doc1',
258
- },
259
- {
260
- input: ['/', '/', '2020/02/29/leap-day'],
261
- output: '/2020/02/29/leap-day',
262
- },
263
- {
264
- input: ['', '/', 'ko', 'hello'],
265
- output: '/ko/hello',
266
- },
267
- {
268
- input: ['hello', 'world'],
269
- output: 'hello/world',
270
- },
271
- {
272
- input: ['http://www.google.com/', 'foo/bar', '?test=123'],
273
- output: 'http://www.google.com/foo/bar?test=123',
274
- },
275
- {
276
- input: ['http:', 'www.google.com///', 'foo/bar', '?test=123'],
277
- output: 'http://www.google.com/foo/bar?test=123',
278
- },
279
- {
280
- input: ['http://foobar.com', '', 'test'],
281
- output: 'http://foobar.com/test',
282
- },
283
- {
284
- input: ['http://foobar.com', '', 'test', '/'],
285
- output: 'http://foobar.com/test/',
286
- },
287
- {
288
- input: ['/', '', 'hello', '', '/', '/', '', '/', '/world'],
289
- output: '/hello/world',
290
- },
291
- {
292
- input: ['', '', '/tt', 'ko', 'hello'],
293
- output: '/tt/ko/hello',
294
- },
295
- {
296
- input: ['', '///hello///', '', '///world'],
297
- output: '/hello/world',
298
- },
299
- {
300
- input: ['', '/hello/', ''],
301
- output: '/hello/',
302
- },
303
- {
304
- input: ['', '/', ''],
305
- output: '/',
306
- },
307
- {
308
- input: ['///', '///'],
309
- output: '/',
310
- },
311
- {
312
- input: ['/', '/hello/world/', '///'],
313
- output: '/hello/world/',
314
- },
315
- ];
316
- asserts.forEach((testCase) => {
317
- expect(normalizeUrl(testCase.input)).toBe(testCase.output);
318
- });
319
-
320
- expect(() =>
321
- // @ts-expect-error undefined for test
322
- normalizeUrl(['http:example.com', undefined]),
323
- ).toThrowErrorMatchingInlineSnapshot(
324
- `"Url must be a string. Received undefined"`,
325
- );
326
- });
327
-
328
220
  test('isValidPathname', () => {
329
221
  expect(isValidPathname('/')).toBe(true);
330
222
  expect(isValidPathname('/hey')).toBe(true);
@@ -459,7 +351,9 @@ describe('mergeTranslations', () => {
459
351
 
460
352
  describe('mapAsyncSequencial', () => {
461
353
  function sleep(timeout: number): Promise<void> {
462
- return new Promise((resolve) => setTimeout(resolve, timeout));
354
+ return new Promise((resolve) => {
355
+ setTimeout(resolve, timeout);
356
+ });
463
357
  }
464
358
 
465
359
  test('map sequentially', async () => {
@@ -498,7 +392,9 @@ describe('mapAsyncSequencial', () => {
498
392
 
499
393
  describe('findAsyncSequencial', () => {
500
394
  function sleep(timeout: number): Promise<void> {
501
- return new Promise((resolve) => setTimeout(resolve, timeout));
395
+ return new Promise((resolve) => {
396
+ setTimeout(resolve, timeout);
397
+ });
502
398
  }
503
399
 
504
400
  test('find sequentially', async () => {
@@ -36,7 +36,7 @@ describe('createExcerpt', () => {
36
36
  Nunc porttitor libero nec vulputate venenatis. Nam nec rhoncus mauris. Morbi tempus est et nibh maximus, tempus venenatis arcu lobortis.
37
37
  `),
38
38
  ).toEqual(
39
- // h1 title is skipped on purpose, because we don't want the page to have SEO metadatas title === description
39
+ // h1 title is skipped on purpose, because we don't want the page to have SEO metadata title === description
40
40
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ex urna, molestie et sagittis ut, varius ac justo.',
41
41
  );
42
42
  });
@@ -53,7 +53,7 @@ describe('createExcerpt', () => {
53
53
  Nunc porttitor libero nec vulputate venenatis. Nam nec rhoncus mauris. Morbi tempus est et nibh maximus, tempus venenatis arcu lobortis.
54
54
  `),
55
55
  ).toEqual(
56
- // h1 title is skipped on purpose, because we don't want the page to have SEO metadatas title === description
56
+ // h1 title is skipped on purpose, because we don't want the page to have SEO metadata title === description
57
57
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ex urna, molestie et sagittis ut, varius ac justo.',
58
58
  );
59
59
  });
@@ -129,6 +129,19 @@ describe('createExcerpt', () => {
129
129
  `),
130
130
  ).toEqual('Markdown title');
131
131
  });
132
+
133
+ test('should create excerpt for content with various code blocks', () => {
134
+ expect(
135
+ createExcerpt(dedent`
136
+ \`\`\`jsx
137
+ import React from 'react';
138
+ import Layout from '@theme/Layout';
139
+ \`\`\`
140
+
141
+ Lorem \`ipsum\` dolor sit amet, consectetur \`adipiscing elit\`.
142
+ `),
143
+ ).toEqual('Lorem ipsum dolor sit amet, consectetur adipiscing elit.');
144
+ });
132
145
  });
133
146
 
134
147
  describe('parseMarkdownContentTitle', () => {
@@ -0,0 +1,133 @@
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 {mdxToHtml} from '../mdxUtils';
9
+
10
+ describe('mdxToHtml', () => {
11
+ test('work with simple markdown', () => {
12
+ const mdxString = `
13
+ # title
14
+
15
+ title text **bold**
16
+
17
+ ## subtitle
18
+
19
+ subtitle text *italic*
20
+
21
+ > Quote
22
+
23
+ `;
24
+
25
+ expect(mdxToHtml(mdxString)).toMatchInlineSnapshot(
26
+ `"<h1>title</h1><p>title text <strong>bold</strong></p><h2>subtitle</h2><p>subtitle text <em>italic</em></p><blockquote><p>Quote</p></blockquote>"`,
27
+ );
28
+ });
29
+
30
+ test('work with MDX imports', () => {
31
+ const mdxString = `
32
+ # title
33
+
34
+ import Tabs from '@theme/Tabs';
35
+ import TabItem from '@theme/TabItem';
36
+
37
+ text
38
+
39
+ `;
40
+
41
+ expect(mdxToHtml(mdxString)).toMatchInlineSnapshot(
42
+ `"<h1>title</h1><p>text</p>"`,
43
+ );
44
+ });
45
+
46
+ test('work with MDX exports', () => {
47
+ const mdxString = `
48
+ # title
49
+
50
+ export const someExport = 42
51
+
52
+ export const MyLocalComponent = () => "result"
53
+
54
+ export const toc = [
55
+ {id: "title",label: "title"}
56
+ ]
57
+
58
+ text
59
+
60
+
61
+ `;
62
+
63
+ expect(mdxToHtml(mdxString)).toMatchInlineSnapshot(
64
+ `"<h1>title</h1><p>text</p>"`,
65
+ );
66
+ });
67
+
68
+ test('work with MDX Tabs', () => {
69
+ const mdxString = `
70
+ # title
71
+
72
+ import Tabs from '@theme/Tabs';
73
+ import TabItem from '@theme/TabItem';
74
+
75
+ <Tabs>
76
+ <TabItem value="apple" label="Apple">
77
+ This is an apple 🍎
78
+ </TabItem>
79
+ <TabItem value="orange" label="Orange">
80
+ This is an orange 🍊
81
+ </TabItem>
82
+ </Tabs>
83
+
84
+ text
85
+
86
+
87
+ `;
88
+
89
+ // TODO this is not an ideal behavior!
90
+ // There is a warning "Component TabItem was not imported, exported, or provided by MDXProvider as global scope"
91
+ // Theme + MDX config should provide a list of React components to put in MDX scope
92
+ expect(mdxToHtml(mdxString)).toMatchInlineSnapshot(
93
+ `"<h1>title</h1><div><div value=\\"apple\\" label=\\"Apple\\">This is an apple 🍎</div><div value=\\"orange\\" label=\\"Orange\\">This is an orange 🍊</div></div><p>text</p>"`,
94
+ );
95
+ });
96
+
97
+ test('work with MDX Tabs with ```mdx-code-block', () => {
98
+ const mdxString = `
99
+ # title
100
+
101
+ import Tabs from '@theme/Tabs';
102
+ import TabItem from '@theme/TabItem';
103
+
104
+ \`\`\`mdx-code-block
105
+ <Tabs>
106
+ <TabItem value="apple" label="Apple">
107
+ This is an apple 🍎
108
+ </TabItem>
109
+ <TabItem value="orange" label="Orange">
110
+ This is an orange 🍊
111
+ </TabItem>
112
+ </Tabs>
113
+ \`\`\`
114
+
115
+ text
116
+
117
+ `;
118
+
119
+ // TODO bad behavior!
120
+ // ```mdx-code-block should be unwrapped and inner MDX content should be evaluated
121
+ expect(mdxToHtml(mdxString)).toMatchInlineSnapshot(`
122
+ "<h1>title</h1><pre><code class=\\"language-mdx-code-block\\">&lt;Tabs&gt;
123
+ &lt;TabItem value=&quot;apple&quot; label=&quot;Apple&quot;&gt;
124
+ This is an apple 🍎
125
+ &lt;/TabItem&gt;
126
+ &lt;TabItem value=&quot;orange&quot; label=&quot;Orange&quot;&gt;
127
+ This is an orange 🍊
128
+ &lt;/TabItem&gt;
129
+ &lt;/Tabs&gt;
130
+ </code></pre><p>text</p>"
131
+ `);
132
+ });
133
+ });