@docusaurus/mdx-loader 2.0.0-beta.138b4c997 → 2.0.0-beta.14
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/index.d.ts +24 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +168 -0
- package/lib/index.js.map +1 -0
- package/lib/remark/headings/index.d.ts +10 -0
- package/lib/remark/headings/index.d.ts.map +1 -0
- package/lib/remark/headings/index.js +61 -0
- package/lib/remark/headings/index.js.map +1 -0
- package/lib/remark/toc/index.d.ts +13 -0
- package/lib/remark/toc/index.d.ts.map +1 -0
- package/lib/remark/toc/index.js +68 -0
- package/lib/remark/toc/index.js.map +1 -0
- package/lib/remark/toc/search.d.ts +14 -0
- package/lib/remark/toc/search.d.ts.map +1 -0
- package/lib/remark/toc/search.js +67 -0
- package/lib/remark/toc/search.js.map +1 -0
- package/lib/remark/transformImage/index.d.ts +14 -0
- package/lib/remark/transformImage/index.d.ts.map +1 -0
- package/lib/remark/transformImage/index.js +91 -0
- package/lib/remark/transformImage/index.js.map +1 -0
- package/lib/remark/transformLinks/index.d.ts +15 -0
- package/lib/remark/transformLinks/index.d.ts.map +1 -0
- package/lib/remark/transformLinks/index.js +107 -0
- package/lib/remark/transformLinks/index.js.map +1 -0
- package/lib/remark/unwrapMdxCodeBlocks/index.d.ts +10 -0
- package/lib/remark/unwrapMdxCodeBlocks/index.d.ts.map +1 -0
- package/lib/remark/unwrapMdxCodeBlocks/index.js +29 -0
- package/lib/remark/unwrapMdxCodeBlocks/index.js.map +1 -0
- package/lib/remark/utils/index.d.ts +11 -0
- package/lib/remark/utils/index.d.ts.map +1 -0
- package/lib/remark/utils/index.js +40 -0
- package/lib/remark/utils/index.js.map +1 -0
- package/package.json +16 -12
- package/src/deps.d.ts +29 -0
- package/src/index.ts +216 -0
- package/src/{index.d.ts → mdx-loader.d.ts} +7 -8
- package/src/remark/headings/{index.js → index.ts} +21 -16
- package/src/remark/toc/{index.js → index.ts} +24 -16
- package/src/remark/toc/search.ts +82 -0
- package/src/remark/transformImage/{index.js → index.ts} +52 -24
- package/src/remark/transformLinks/{index.js → index.ts} +66 -44
- package/src/remark/unwrapMdxCodeBlocks/{index.js → index.ts} +11 -9
- package/src/remark/utils/index.ts +39 -0
- package/src/index.js +0 -107
- package/src/remark/headings/__tests__/index.test.js +0 -315
- package/src/remark/toc/__tests__/__snapshots__/index.test.js.snap +0 -85
- package/src/remark/toc/__tests__/fixtures/empty-headings.md +0 -5
- package/src/remark/toc/__tests__/fixtures/inline-code.md +0 -9
- package/src/remark/toc/__tests__/fixtures/insert-below-imports.md +0 -11
- package/src/remark/toc/__tests__/fixtures/just-content.md +0 -15
- package/src/remark/toc/__tests__/fixtures/name-exist.md +0 -7
- package/src/remark/toc/__tests__/fixtures/non-text-content.md +0 -9
- package/src/remark/toc/__tests__/index.test.js +0 -212
- package/src/remark/toc/search.js +0 -73
- package/src/remark/transformImage/__tests__/__snapshots__/index.test.js.snap +0 -31
- package/src/remark/transformImage/__tests__/fixtures/fail.md +0 -1
- package/src/remark/transformImage/__tests__/fixtures/img.md +0 -17
- package/src/remark/transformImage/__tests__/fixtures/img.png +0 -0
- package/src/remark/transformImage/__tests__/fixtures/noUrl.md +0 -1
- package/src/remark/transformImage/__tests__/fixtures/pathname.md +0 -1
- package/src/remark/transformImage/__tests__/index.test.js +0 -59
- package/src/remark/transformLinks/__tests__/__snapshots__/index.test.js.snap +0 -41
- package/src/remark/transformLinks/__tests__/fixtures/asset.md +0 -29
- package/src/remark/transformLinks/__tests__/fixtures/asset.pdf +0 -0
- package/src/remark/transformLinks/__tests__/fixtures/noUrl.md +0 -1
- package/src/remark/transformLinks/__tests__/fixtures/pathname.md +0 -1
- package/src/remark/transformLinks/__tests__/fixtures/static/staticAsset.pdf +0 -0
- package/src/remark/transformLinks/__tests__/fixtures/static/staticAssetImage.png +0 -0
- package/src/remark/transformLinks/__tests__/index.test.js +0 -44
- package/src/remark/unwrapMdxCodeBlocks/__tests__/__snapshots__/index.test.js.snap +0 -819
- package/src/remark/unwrapMdxCodeBlocks/__tests__/fixtures/has-mdx-code-blocks.mdx +0 -95
- package/src/remark/unwrapMdxCodeBlocks/__tests__/index.test.js +0 -38
- package/src/remark/utils/index.js +0 -39
- package/tsconfig.json +0 -9
|
@@ -5,34 +5,42 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import {parse, ParserOptions} from '@babel/parser';
|
|
9
|
+
import type {Identifier} from '@babel/types';
|
|
10
|
+
import traverse from '@babel/traverse';
|
|
11
|
+
import stringifyObject from 'stringify-object';
|
|
12
|
+
import search from './search';
|
|
13
|
+
import type {Plugin, Transformer} from 'unified';
|
|
14
|
+
import type {Node, Parent} from 'unist';
|
|
15
|
+
import type {Literal} from 'mdast';
|
|
12
16
|
|
|
13
|
-
const parseOptions = {
|
|
17
|
+
const parseOptions: ParserOptions = {
|
|
14
18
|
plugins: ['jsx'],
|
|
15
19
|
sourceType: 'module',
|
|
16
20
|
};
|
|
17
|
-
const isImport = (child) => child.type === 'import';
|
|
18
|
-
const hasImports = (index) => index > -1;
|
|
19
|
-
const isExport = (child) => child.type === 'export';
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
+
const isImport = (child: Node): child is Literal => child.type === 'import';
|
|
23
|
+
const hasImports = (index: number) => index > -1;
|
|
24
|
+
const isExport = (child: Node): child is Literal => child.type === 'export';
|
|
25
|
+
|
|
26
|
+
interface PluginOptions {
|
|
27
|
+
name?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const isTarget = (child: Literal, name: string) => {
|
|
22
31
|
let found = false;
|
|
23
32
|
const ast = parse(child.value, parseOptions);
|
|
24
33
|
traverse(ast, {
|
|
25
34
|
VariableDeclarator: (path) => {
|
|
26
|
-
if (path.node.id.name === name) {
|
|
35
|
+
if ((path.node.id as Identifier).name === name) {
|
|
27
36
|
found = true;
|
|
28
37
|
}
|
|
29
38
|
},
|
|
30
39
|
});
|
|
31
|
-
|
|
32
40
|
return found;
|
|
33
41
|
};
|
|
34
42
|
|
|
35
|
-
const getOrCreateExistingTargetIndex = (children, name) => {
|
|
43
|
+
const getOrCreateExistingTargetIndex = (children: Node[], name: string) => {
|
|
36
44
|
let importsIndex = -1;
|
|
37
45
|
let targetIndex = -1;
|
|
38
46
|
|
|
@@ -58,12 +66,12 @@ const getOrCreateExistingTargetIndex = (children, name) => {
|
|
|
58
66
|
return targetIndex;
|
|
59
67
|
};
|
|
60
68
|
|
|
61
|
-
const plugin = (options = {}) => {
|
|
69
|
+
const plugin: Plugin<[PluginOptions?]> = (options = {}) => {
|
|
62
70
|
const name = options.name || 'toc';
|
|
63
71
|
|
|
64
|
-
const transformer = (node) => {
|
|
72
|
+
const transformer: Transformer = (node) => {
|
|
65
73
|
const headings = search(node);
|
|
66
|
-
const {children} = node
|
|
74
|
+
const {children} = node as Parent<Literal>;
|
|
67
75
|
const targetIndex = getOrCreateExistingTargetIndex(children, name);
|
|
68
76
|
|
|
69
77
|
if (headings && headings.length) {
|
|
@@ -76,4 +84,4 @@ const plugin = (options = {}) => {
|
|
|
76
84
|
return transformer;
|
|
77
85
|
};
|
|
78
86
|
|
|
79
|
-
|
|
87
|
+
export default plugin;
|
|
@@ -0,0 +1,82 @@
|
|
|
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 toString from 'mdast-util-to-string';
|
|
9
|
+
import visit, {Visitor} from 'unist-util-visit';
|
|
10
|
+
import {toValue} from '../utils';
|
|
11
|
+
import type {TOCItem} from '@docusaurus/types';
|
|
12
|
+
import type {Node} from 'unist';
|
|
13
|
+
import type {Heading} from 'mdast';
|
|
14
|
+
|
|
15
|
+
// Intermediate interface for TOC algorithm
|
|
16
|
+
interface SearchItem {
|
|
17
|
+
node: TOCItem;
|
|
18
|
+
level: number;
|
|
19
|
+
parentIndex: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* Generate a TOC AST from the raw Markdown contents
|
|
25
|
+
*/
|
|
26
|
+
export default function search(node: Node): TOCItem[] {
|
|
27
|
+
const headings: SearchItem[] = [];
|
|
28
|
+
|
|
29
|
+
const visitor: Visitor<Heading> = (child, _index, parent) => {
|
|
30
|
+
const value = toString(child);
|
|
31
|
+
|
|
32
|
+
// depth:1 headings are titles and not included in the TOC
|
|
33
|
+
if (parent !== node || !value || child.depth < 2) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
headings.push({
|
|
38
|
+
node: {
|
|
39
|
+
value: toValue(child),
|
|
40
|
+
id: child.data!.id as string,
|
|
41
|
+
children: [],
|
|
42
|
+
level: child.depth,
|
|
43
|
+
},
|
|
44
|
+
level: child.depth,
|
|
45
|
+
parentIndex: -1,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
visit(node, 'heading', visitor);
|
|
50
|
+
|
|
51
|
+
// Keep track of which previous index would be the current heading's direcy parent.
|
|
52
|
+
// Each entry <i> is the last index of the `headings` array at heading level <i>.
|
|
53
|
+
// We will modify these indices as we iterate through all headings.
|
|
54
|
+
// e.g. if an ### H3 was last seen at index 2, then prevIndexForLevel[3] === 2
|
|
55
|
+
// indices 0 and 1 will remain unused.
|
|
56
|
+
const prevIndexForLevel = Array(7).fill(-1);
|
|
57
|
+
|
|
58
|
+
headings.forEach((curr, currIndex) => {
|
|
59
|
+
// take the last seen index for each ancestor level. the highest
|
|
60
|
+
// index will be the direct ancestor of the current heading.
|
|
61
|
+
const ancestorLevelIndexes = prevIndexForLevel.slice(2, curr.level);
|
|
62
|
+
curr.parentIndex = Math.max(...ancestorLevelIndexes);
|
|
63
|
+
// mark that curr.level was last seen at the current index
|
|
64
|
+
prevIndexForLevel[curr.level] = currIndex;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const rootNodeIndexes: number[] = [];
|
|
68
|
+
|
|
69
|
+
// For a given parentIndex, add each Node into that parent's `children` array
|
|
70
|
+
headings.forEach((heading, i) => {
|
|
71
|
+
if (heading.parentIndex >= 0) {
|
|
72
|
+
headings[heading.parentIndex].node.children.push(heading.node);
|
|
73
|
+
} else {
|
|
74
|
+
rootNodeIndexes.push(i);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const toc = headings
|
|
79
|
+
.filter((_, k) => rootNodeIndexes.includes(k)) // only return root nodes
|
|
80
|
+
.map((heading) => heading.node); // only return Node, no metadata
|
|
81
|
+
return toc;
|
|
82
|
+
}
|
|
@@ -5,26 +5,35 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const {
|
|
8
|
+
import visit from 'unist-util-visit';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import url from 'url';
|
|
11
|
+
import fs from 'fs-extra';
|
|
12
|
+
import escapeHtml from 'escape-html';
|
|
13
|
+
import {
|
|
15
14
|
posixPath,
|
|
16
15
|
escapePath,
|
|
17
16
|
toMessageRelativeFilePath,
|
|
18
|
-
|
|
17
|
+
getFileLoaderUtils,
|
|
18
|
+
} from '@docusaurus/utils';
|
|
19
|
+
import type {Plugin, Transformer} from 'unified';
|
|
20
|
+
import type {Image, Literal} from 'mdast';
|
|
19
21
|
|
|
20
22
|
const {
|
|
21
23
|
loaders: {inlineMarkdownImageFileLoader},
|
|
22
24
|
} = getFileLoaderUtils();
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
interface PluginOptions {
|
|
27
|
+
filePath: string;
|
|
28
|
+
staticDirs: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const createJSX = (node: Image, pathUrl: string) => {
|
|
25
32
|
const jsxNode = node;
|
|
26
|
-
jsxNode.type = 'jsx';
|
|
27
|
-
jsxNode.value = `<img ${
|
|
33
|
+
(jsxNode as unknown as Literal).type = 'jsx';
|
|
34
|
+
(jsxNode as unknown as Literal).value = `<img ${
|
|
35
|
+
node.alt ? `alt={"${escapeHtml(node.alt)}"} ` : ''
|
|
36
|
+
}${
|
|
28
37
|
node.url
|
|
29
38
|
? `src={require("${inlineMarkdownImageFileLoader}${escapePath(
|
|
30
39
|
pathUrl,
|
|
@@ -33,7 +42,7 @@ const createJSX = (node, pathUrl) => {
|
|
|
33
42
|
}${node.title ? ` title="${escapeHtml(node.title)}"` : ''} />`;
|
|
34
43
|
|
|
35
44
|
if (jsxNode.url) {
|
|
36
|
-
delete jsxNode.url;
|
|
45
|
+
delete (jsxNode as Partial<Image>).url;
|
|
37
46
|
}
|
|
38
47
|
if (jsxNode.alt) {
|
|
39
48
|
delete jsxNode.alt;
|
|
@@ -43,7 +52,7 @@ const createJSX = (node, pathUrl) => {
|
|
|
43
52
|
}
|
|
44
53
|
};
|
|
45
54
|
|
|
46
|
-
async function ensureImageFileExist(imagePath, sourceFilePath) {
|
|
55
|
+
async function ensureImageFileExist(imagePath: string, sourceFilePath: string) {
|
|
47
56
|
const imageExists = await fs.pathExists(imagePath);
|
|
48
57
|
if (!imageExists) {
|
|
49
58
|
throw new Error(
|
|
@@ -54,7 +63,26 @@ async function ensureImageFileExist(imagePath, sourceFilePath) {
|
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
|
|
57
|
-
async function
|
|
66
|
+
async function findImage(possiblePaths: string[], sourceFilePath: string) {
|
|
67
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
68
|
+
for (const possiblePath of possiblePaths) {
|
|
69
|
+
if (await fs.pathExists(possiblePath)) {
|
|
70
|
+
return possiblePath;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Image ${possiblePaths
|
|
75
|
+
.map((p) => toMessageRelativeFilePath(p))
|
|
76
|
+
.join(' or ')} used in ${toMessageRelativeFilePath(
|
|
77
|
+
sourceFilePath,
|
|
78
|
+
)} not found.`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function processImageNode(
|
|
83
|
+
node: Image,
|
|
84
|
+
{filePath, staticDirs}: PluginOptions,
|
|
85
|
+
) {
|
|
58
86
|
if (!node.url) {
|
|
59
87
|
throw new Error(
|
|
60
88
|
`Markdown image URL is mandatory in "${toMessageRelativeFilePath(
|
|
@@ -71,16 +99,16 @@ async function processImageNode(node, {filePath, staticDir}) {
|
|
|
71
99
|
// it's mostly to make next release less risky (2.0.0-alpha.59)
|
|
72
100
|
if (parsedUrl.protocol === 'pathname:') {
|
|
73
101
|
node.url = node.url.replace('pathname://', '');
|
|
74
|
-
} else {
|
|
75
|
-
// noop
|
|
76
102
|
}
|
|
77
103
|
}
|
|
78
104
|
// images without protocol
|
|
79
105
|
else if (path.isAbsolute(node.url)) {
|
|
80
106
|
// absolute paths are expected to exist in the static folder
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
const possibleImagePaths = staticDirs.map((dir) =>
|
|
108
|
+
path.join(dir, node.url),
|
|
109
|
+
);
|
|
110
|
+
const imagePath = await findImage(possibleImagePaths, filePath);
|
|
111
|
+
createJSX(node, posixPath(imagePath));
|
|
84
112
|
}
|
|
85
113
|
// We try to convert image urls without protocol to images with require calls
|
|
86
114
|
// going through webpack ensures that image assets exist at build time
|
|
@@ -92,10 +120,10 @@ async function processImageNode(node, {filePath, staticDir}) {
|
|
|
92
120
|
}
|
|
93
121
|
}
|
|
94
122
|
|
|
95
|
-
const plugin = (options) => {
|
|
96
|
-
const transformer = async (root) => {
|
|
97
|
-
const promises = [];
|
|
98
|
-
visit(root, 'image', (node) => {
|
|
123
|
+
const plugin: Plugin<[PluginOptions]> = (options) => {
|
|
124
|
+
const transformer: Transformer = async (root) => {
|
|
125
|
+
const promises: Promise<void>[] = [];
|
|
126
|
+
visit(root, 'image', (node: Image) => {
|
|
99
127
|
promises.push(processImageNode(node, options));
|
|
100
128
|
});
|
|
101
129
|
await Promise.all(promises);
|
|
@@ -103,4 +131,4 @@ const plugin = (options) => {
|
|
|
103
131
|
return transformer;
|
|
104
132
|
};
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
export default plugin;
|
|
@@ -5,25 +5,36 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import {
|
|
9
9
|
toMessageRelativeFilePath,
|
|
10
10
|
posixPath,
|
|
11
11
|
escapePath,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
getFileLoaderUtils,
|
|
13
|
+
} from '@docusaurus/utils';
|
|
14
|
+
import visit from 'unist-util-visit';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import url from 'url';
|
|
17
|
+
import fs from 'fs-extra';
|
|
18
|
+
import escapeHtml from 'escape-html';
|
|
19
|
+
import {stringifyContent} from '../utils';
|
|
20
|
+
import type {Plugin, Transformer} from 'unified';
|
|
21
|
+
import type {Link, Literal} from 'mdast';
|
|
21
22
|
|
|
22
23
|
const {
|
|
23
24
|
loaders: {inlineMarkdownLinkFileLoader},
|
|
24
25
|
} = getFileLoaderUtils();
|
|
26
|
+
const hashRegex = /#.*$/;
|
|
27
|
+
|
|
28
|
+
interface PluginOptions {
|
|
29
|
+
filePath: string;
|
|
30
|
+
staticDirs: string[];
|
|
31
|
+
siteDir: string;
|
|
32
|
+
}
|
|
25
33
|
|
|
26
|
-
async function ensureAssetFileExist(
|
|
34
|
+
async function ensureAssetFileExist(
|
|
35
|
+
fileSystemAssetPath: string,
|
|
36
|
+
sourceFilePath: string,
|
|
37
|
+
) {
|
|
27
38
|
const assetExists = await fs.pathExists(fileSystemAssetPath);
|
|
28
39
|
if (!assetExists) {
|
|
29
40
|
throw new Error(
|
|
@@ -35,12 +46,21 @@ async function ensureAssetFileExist(fileSystemAssetPath, sourceFilePath) {
|
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
// transform the link node to a jsx link with a require() call
|
|
38
|
-
function toAssetRequireNode({
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
function toAssetRequireNode({
|
|
50
|
+
node,
|
|
51
|
+
filePath,
|
|
52
|
+
requireAssetPath,
|
|
53
|
+
}: {
|
|
54
|
+
node: Link;
|
|
55
|
+
filePath: string;
|
|
56
|
+
requireAssetPath: string;
|
|
57
|
+
}) {
|
|
41
58
|
let relativeRequireAssetPath = posixPath(
|
|
42
59
|
path.relative(path.dirname(filePath), requireAssetPath),
|
|
43
60
|
);
|
|
61
|
+
const hash = hashRegex.test(node.url)
|
|
62
|
+
? node.url.substr(node.url.indexOf('#'))
|
|
63
|
+
: '';
|
|
44
64
|
|
|
45
65
|
// nodejs does not like require("assets/file.pdf")
|
|
46
66
|
relativeRequireAssetPath = relativeRequireAssetPath.startsWith('.')
|
|
@@ -49,19 +69,24 @@ function toAssetRequireNode({node, filePath, requireAssetPath}) {
|
|
|
49
69
|
|
|
50
70
|
const href = `require('${inlineMarkdownLinkFileLoader}${escapePath(
|
|
51
71
|
relativeRequireAssetPath,
|
|
52
|
-
)}').default`;
|
|
53
|
-
const children = (node
|
|
54
|
-
const title = node.title ? `title="${escapeHtml(node.title)}"` : '';
|
|
55
|
-
|
|
56
|
-
node.type = 'jsx';
|
|
57
|
-
|
|
72
|
+
)}').default${hash ? ` + '${hash}'` : ''}`;
|
|
73
|
+
const children = stringifyContent(node);
|
|
74
|
+
const title = node.title ? ` title="${escapeHtml(node.title)}"` : '';
|
|
75
|
+
|
|
76
|
+
(node as unknown as Literal).type = 'jsx';
|
|
77
|
+
(
|
|
78
|
+
node as unknown as Literal
|
|
79
|
+
).value = `<a target="_blank" href={${href}}${title}>${children}</a>`;
|
|
58
80
|
}
|
|
59
81
|
|
|
60
82
|
// If the link looks like an asset link, we'll link to the asset,
|
|
61
83
|
// and use a require("assetUrl") (using webpack url-loader/file-loader)
|
|
62
84
|
// instead of navigating to such link
|
|
63
|
-
async function convertToAssetLinkIfNeeded(
|
|
64
|
-
|
|
85
|
+
async function convertToAssetLinkIfNeeded(
|
|
86
|
+
node: Link,
|
|
87
|
+
{filePath, siteDir, staticDirs}: PluginOptions,
|
|
88
|
+
) {
|
|
89
|
+
const assetPath = node.url.replace(hashRegex, '');
|
|
65
90
|
|
|
66
91
|
const hasSiteAlias = assetPath.startsWith('@site/');
|
|
67
92
|
const hasAssetLikeExtension =
|
|
@@ -73,7 +98,7 @@ async function convertToAssetLinkIfNeeded({node, staticDir, filePath}) {
|
|
|
73
98
|
return;
|
|
74
99
|
}
|
|
75
100
|
|
|
76
|
-
function toAssetLinkNode(requireAssetPath) {
|
|
101
|
+
function toAssetLinkNode(requireAssetPath: string) {
|
|
77
102
|
toAssetRequireNode({
|
|
78
103
|
node,
|
|
79
104
|
filePath,
|
|
@@ -82,7 +107,6 @@ async function convertToAssetLinkIfNeeded({node, staticDir, filePath}) {
|
|
|
82
107
|
}
|
|
83
108
|
|
|
84
109
|
if (assetPath.startsWith('@site/')) {
|
|
85
|
-
const siteDir = path.join(staticDir, '..');
|
|
86
110
|
const fileSystemAssetPath = path.join(
|
|
87
111
|
siteDir,
|
|
88
112
|
assetPath.replace('@site/', ''),
|
|
@@ -90,9 +114,13 @@ async function convertToAssetLinkIfNeeded({node, staticDir, filePath}) {
|
|
|
90
114
|
await ensureAssetFileExist(fileSystemAssetPath, filePath);
|
|
91
115
|
toAssetLinkNode(fileSystemAssetPath);
|
|
92
116
|
} else if (path.isAbsolute(assetPath)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
118
|
+
for (const staticDir of staticDirs) {
|
|
119
|
+
const fileSystemAssetPath = path.join(staticDir, assetPath);
|
|
120
|
+
if (await fs.pathExists(fileSystemAssetPath)) {
|
|
121
|
+
toAssetLinkNode(fileSystemAssetPath);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
96
124
|
}
|
|
97
125
|
} else {
|
|
98
126
|
const fileSystemAssetPath = path.join(path.dirname(filePath), assetPath);
|
|
@@ -102,17 +130,15 @@ async function convertToAssetLinkIfNeeded({node, staticDir, filePath}) {
|
|
|
102
130
|
}
|
|
103
131
|
}
|
|
104
132
|
|
|
105
|
-
async function processLinkNode(
|
|
133
|
+
async function processLinkNode(node: Link, options: PluginOptions) {
|
|
106
134
|
if (!node.url) {
|
|
107
135
|
// try to improve error feedback
|
|
108
136
|
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
|
|
109
|
-
const title =
|
|
110
|
-
|
|
111
|
-
const line =
|
|
112
|
-
(node.position && node.position.start && node.position.start.line) || '?';
|
|
137
|
+
const title = node.title || (node.children[0] as Literal)?.value || '?';
|
|
138
|
+
const line = node?.position?.start?.line || '?';
|
|
113
139
|
throw new Error(
|
|
114
140
|
`Markdown link URL is mandatory in "${toMessageRelativeFilePath(
|
|
115
|
-
filePath,
|
|
141
|
+
options.filePath,
|
|
116
142
|
)}" file (title: ${title}, line: ${line}).`,
|
|
117
143
|
);
|
|
118
144
|
}
|
|
@@ -122,22 +148,18 @@ async function processLinkNode({node, _index, _parent, filePath, staticDir}) {
|
|
|
122
148
|
return;
|
|
123
149
|
}
|
|
124
150
|
|
|
125
|
-
await convertToAssetLinkIfNeeded(
|
|
126
|
-
node,
|
|
127
|
-
staticDir,
|
|
128
|
-
filePath,
|
|
129
|
-
});
|
|
151
|
+
await convertToAssetLinkIfNeeded(node, options);
|
|
130
152
|
}
|
|
131
153
|
|
|
132
|
-
const plugin = (options) => {
|
|
133
|
-
const transformer = async (root) => {
|
|
134
|
-
const promises = [];
|
|
135
|
-
visit(root, 'link', (node
|
|
136
|
-
promises.push(processLinkNode(
|
|
154
|
+
const plugin: Plugin<[PluginOptions]> = (options) => {
|
|
155
|
+
const transformer: Transformer = async (root) => {
|
|
156
|
+
const promises: Promise<void>[] = [];
|
|
157
|
+
visit(root, 'link', (node: Link) => {
|
|
158
|
+
promises.push(processLinkNode(node, options));
|
|
137
159
|
});
|
|
138
160
|
await Promise.all(promises);
|
|
139
161
|
};
|
|
140
162
|
return transformer;
|
|
141
163
|
};
|
|
142
164
|
|
|
143
|
-
|
|
165
|
+
export default plugin;
|
|
@@ -5,24 +5,26 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import visit from 'unist-util-visit';
|
|
9
|
+
import type {Transformer, Processor} from 'unified';
|
|
10
|
+
import type {Code, Parent} from 'mdast';
|
|
9
11
|
|
|
10
12
|
// This plugin is mostly to help integrating Docusaurus with translation systems
|
|
11
13
|
// that do not support well MDX embedded JSX syntax (like Crowdin)
|
|
12
14
|
// We wrap the JSX syntax in code blocks so that translation tools don't mess-up with the markup
|
|
13
15
|
// But the JSX inside such code blocks should still be evaluated as JSX
|
|
14
16
|
// See https://github.com/facebook/docusaurus/pull/4278
|
|
15
|
-
function plugin() {
|
|
16
|
-
const transformer = (root) => {
|
|
17
|
-
visit(root, 'code', (node, _index, parent) => {
|
|
17
|
+
function plugin(this: Processor): Transformer {
|
|
18
|
+
const transformer: Transformer = (root) => {
|
|
19
|
+
visit(root, 'code', (node: Code, _index, parent) => {
|
|
18
20
|
if (node.lang === 'mdx-code-block') {
|
|
19
|
-
const
|
|
21
|
+
const newChildren = (this!.parse(node.value) as Parent).children;
|
|
20
22
|
|
|
21
23
|
// Replace the mdx code block by its content, parsed
|
|
22
|
-
parent
|
|
23
|
-
parent
|
|
24
|
+
parent!.children.splice(
|
|
25
|
+
parent!.children.indexOf(node),
|
|
24
26
|
1,
|
|
25
|
-
...
|
|
27
|
+
...newChildren,
|
|
26
28
|
);
|
|
27
29
|
}
|
|
28
30
|
});
|
|
@@ -31,4 +33,4 @@ function plugin() {
|
|
|
31
33
|
return transformer;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
export default plugin;
|
|
@@ -0,0 +1,39 @@
|
|
|
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 escapeHtml from 'escape-html';
|
|
9
|
+
import toString from 'mdast-util-to-string';
|
|
10
|
+
import type {Parent} from 'unist';
|
|
11
|
+
import type {StaticPhrasingContent, Heading} from 'mdast';
|
|
12
|
+
|
|
13
|
+
export function stringifyContent(node: Parent): string {
|
|
14
|
+
return ((node.children || []) as StaticPhrasingContent[])
|
|
15
|
+
.map(toValue)
|
|
16
|
+
.join('');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function toValue(node: StaticPhrasingContent | Heading): string {
|
|
20
|
+
if (node && node.type) {
|
|
21
|
+
switch (node.type) {
|
|
22
|
+
case 'text':
|
|
23
|
+
return escapeHtml(node.value);
|
|
24
|
+
case 'heading':
|
|
25
|
+
return stringifyContent(node);
|
|
26
|
+
case 'inlineCode':
|
|
27
|
+
return `<code>${escapeHtml(node.value)}</code>`;
|
|
28
|
+
case 'emphasis':
|
|
29
|
+
return `<em>${stringifyContent(node)}</em>`;
|
|
30
|
+
case 'strong':
|
|
31
|
+
return `<strong>${stringifyContent(node)}</strong>`;
|
|
32
|
+
case 'delete':
|
|
33
|
+
return `<del>${stringifyContent(node)}</del>`;
|
|
34
|
+
default:
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return toString(node);
|
|
39
|
+
}
|
package/src/index.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
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
|
-
const {readFile} = require('fs-extra');
|
|
9
|
-
const mdx = require('@mdx-js/mdx');
|
|
10
|
-
const emoji = require('remark-emoji');
|
|
11
|
-
const {
|
|
12
|
-
parseFrontMatter,
|
|
13
|
-
parseMarkdownContentTitle,
|
|
14
|
-
} = require('@docusaurus/utils');
|
|
15
|
-
const stringifyObject = require('stringify-object');
|
|
16
|
-
const headings = require('./remark/headings');
|
|
17
|
-
const toc = require('./remark/toc');
|
|
18
|
-
const unwrapMdxCodeBlocks = require('./remark/unwrapMdxCodeBlocks');
|
|
19
|
-
const transformImage = require('./remark/transformImage');
|
|
20
|
-
const transformLinks = require('./remark/transformLinks');
|
|
21
|
-
|
|
22
|
-
const DEFAULT_OPTIONS = {
|
|
23
|
-
rehypePlugins: [],
|
|
24
|
-
remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc],
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
module.exports = async function docusaurusMdxLoader(fileString) {
|
|
28
|
-
const callback = this.async();
|
|
29
|
-
|
|
30
|
-
const reqOptions = this.getOptions() || {};
|
|
31
|
-
|
|
32
|
-
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);
|
|
33
|
-
|
|
34
|
-
const {content, contentTitle} = parseMarkdownContentTitle(contentWithTitle, {
|
|
35
|
-
removeContentTitle: reqOptions.removeContentTitle,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const hasFrontMatter = Object.keys(frontMatter).length > 0;
|
|
39
|
-
|
|
40
|
-
const options = {
|
|
41
|
-
...reqOptions,
|
|
42
|
-
remarkPlugins: [
|
|
43
|
-
...(reqOptions.beforeDefaultRemarkPlugins || []),
|
|
44
|
-
...DEFAULT_OPTIONS.remarkPlugins,
|
|
45
|
-
[
|
|
46
|
-
transformImage,
|
|
47
|
-
{staticDir: reqOptions.staticDir, filePath: this.resourcePath},
|
|
48
|
-
],
|
|
49
|
-
[
|
|
50
|
-
transformLinks,
|
|
51
|
-
{staticDir: reqOptions.staticDir, filePath: this.resourcePath},
|
|
52
|
-
],
|
|
53
|
-
...(reqOptions.remarkPlugins || []),
|
|
54
|
-
],
|
|
55
|
-
rehypePlugins: [
|
|
56
|
-
...(reqOptions.beforeDefaultRehypePlugins || []),
|
|
57
|
-
...DEFAULT_OPTIONS.rehypePlugins,
|
|
58
|
-
|
|
59
|
-
...(reqOptions.rehypePlugins || []),
|
|
60
|
-
],
|
|
61
|
-
filepath: this.resourcePath,
|
|
62
|
-
};
|
|
63
|
-
let result;
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
result = await mdx(content, options);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
return callback(err);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
let exportStr = ``;
|
|
72
|
-
exportStr += `\nexport const frontMatter = ${stringifyObject(frontMatter)};`;
|
|
73
|
-
exportStr += `\nexport const contentTitle = ${stringifyObject(
|
|
74
|
-
contentTitle,
|
|
75
|
-
)};`;
|
|
76
|
-
|
|
77
|
-
// Read metadata for this MDX and export it.
|
|
78
|
-
if (options.metadataPath && typeof options.metadataPath === 'function') {
|
|
79
|
-
const metadataPath = options.metadataPath(this.resourcePath);
|
|
80
|
-
|
|
81
|
-
if (metadataPath) {
|
|
82
|
-
// Add as dependency of this loader result so that we can
|
|
83
|
-
// recompile if metadata is changed.
|
|
84
|
-
this.addDependency(metadataPath);
|
|
85
|
-
const metadata = await readFile(metadataPath, 'utf8');
|
|
86
|
-
exportStr += `\nexport const metadata = ${metadata};`;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
options.forbidFrontMatter &&
|
|
92
|
-
typeof options.forbidFrontMatter === 'function'
|
|
93
|
-
) {
|
|
94
|
-
if (options.forbidFrontMatter(this.resourcePath) && hasFrontMatter) {
|
|
95
|
-
return callback(new Error(`Front matter is forbidden in this file`));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const code = `
|
|
99
|
-
import React from 'react';
|
|
100
|
-
import { mdx } from '@mdx-js/react';
|
|
101
|
-
|
|
102
|
-
${exportStr}
|
|
103
|
-
${result}
|
|
104
|
-
`;
|
|
105
|
-
|
|
106
|
-
return callback(null, code);
|
|
107
|
-
};
|