@astrojs/mdx 0.5.0 → 0.8.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +30 -0
- package/README.md +57 -76
- package/dist/astro-data-utils.d.ts +9 -0
- package/dist/astro-data-utils.js +47 -0
- package/dist/index.d.ts +0 -7
- package/dist/index.js +49 -44
- package/dist/utils.d.ts +2 -3
- package/dist/utils.js +4 -4
- package/package.json +4 -5
- package/src/astro-data-utils.ts +59 -0
- package/src/index.ts +63 -59
- package/src/utils.ts +2 -2
- package/test/fixtures/mdx-escape/src/components/Em.astro +7 -0
- package/test/fixtures/mdx-escape/src/components/P.astro +1 -0
- package/test/fixtures/mdx-escape/src/components/Title.astro +1 -0
- package/test/fixtures/mdx-escape/src/pages/html-tag.mdx +5 -0
- package/test/fixtures/mdx-escape/src/pages/index.mdx +13 -0
- package/test/fixtures/mdx-frontmatter/src/layouts/Base.astro +13 -3
- package/test/fixtures/mdx-frontmatter/src/pages/index.mdx +3 -0
- package/test/fixtures/mdx-frontmatter/src/pages/with-headings.mdx +7 -0
- package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +12 -0
- package/test/fixtures/mdx-frontmatter-injection/node_modules/.bin/astro +17 -0
- package/test/fixtures/mdx-frontmatter-injection/package.json +12 -0
- package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +20 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/glob.json.js +6 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +3 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +19 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx +7 -0
- package/test/fixtures/mdx-plus-react/astro.config.mjs +6 -0
- package/test/fixtures/mdx-plus-react/node_modules/.bin/astro +17 -0
- package/test/fixtures/mdx-plus-react/package.json +8 -0
- package/test/fixtures/mdx-plus-react/src/components/Component.jsx +5 -0
- package/test/fixtures/mdx-plus-react/src/pages/index.astro +11 -0
- package/test/fixtures/mdx-rehype-plugins/src/pages/reading-time.json.js +2 -2
- package/test/fixtures/mdx-remark-plugins/src/pages/headings-glob.json.js +6 -0
- package/test/mdx-escape.test.js +32 -0
- package/test/mdx-frontmatter-injection.test.js +44 -0
- package/test/mdx-frontmatter.test.js +20 -37
- package/test/mdx-plus-react.test.js +25 -0
- package/test/mdx-rehype-plugins.test.js +6 -17
- package/test/fixtures/mdx-custom-frontmatter-name/src/pages/glob.json.js +0 -9
- package/test/fixtures/mdx-custom-frontmatter-name/src/pages/index.mdx +0 -6
package/src/index.ts
CHANGED
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
import { compile as mdxCompile, nodeTypes } from '@mdx-js/mdx';
|
|
2
2
|
import mdxPlugin, { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
|
|
3
|
-
import type { AstroIntegration } from 'astro';
|
|
3
|
+
import type { AstroConfig, AstroIntegration } from 'astro';
|
|
4
4
|
import { parse as parseESM } from 'es-module-lexer';
|
|
5
5
|
import rehypeRaw from 'rehype-raw';
|
|
6
|
-
import remarkFrontmatter from 'remark-frontmatter';
|
|
7
6
|
import remarkGfm from 'remark-gfm';
|
|
8
|
-
import type { RemarkMdxFrontmatterOptions } from 'remark-mdx-frontmatter';
|
|
9
|
-
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
|
|
10
7
|
import remarkShikiTwoslash from 'remark-shiki-twoslash';
|
|
11
8
|
import remarkSmartypants from 'remark-smartypants';
|
|
12
9
|
import { VFile } from 'vfile';
|
|
13
10
|
import type { Plugin as VitePlugin } from 'vite';
|
|
11
|
+
import { rehypeApplyFrontmatterExport, remarkInitializeAstroData } from './astro-data-utils.js';
|
|
14
12
|
import rehypeCollectHeadings from './rehype-collect-headings.js';
|
|
15
13
|
import remarkPrism from './remark-prism.js';
|
|
16
|
-
import { getFileInfo,
|
|
14
|
+
import { getFileInfo, parseFrontmatter } from './utils.js';
|
|
17
15
|
|
|
18
16
|
type WithExtends<T> = T | { extends: T };
|
|
19
17
|
|
|
20
18
|
type MdxOptions = {
|
|
21
19
|
remarkPlugins?: WithExtends<MdxRollupPluginOptions['remarkPlugins']>;
|
|
22
20
|
rehypePlugins?: WithExtends<MdxRollupPluginOptions['rehypePlugins']>;
|
|
23
|
-
/**
|
|
24
|
-
* Configure the remark-mdx-frontmatter plugin
|
|
25
|
-
* @see https://github.com/remcohaszing/remark-mdx-frontmatter#options for a full list of options
|
|
26
|
-
* @default {{ name: 'frontmatter' }}
|
|
27
|
-
*/
|
|
28
|
-
frontmatterOptions?: RemarkMdxFrontmatterOptions;
|
|
29
21
|
};
|
|
30
22
|
|
|
31
|
-
const DEFAULT_REMARK_PLUGINS = [
|
|
32
|
-
|
|
23
|
+
const DEFAULT_REMARK_PLUGINS: MdxRollupPluginOptions['remarkPlugins'] = [
|
|
24
|
+
remarkGfm,
|
|
25
|
+
remarkSmartypants,
|
|
26
|
+
];
|
|
27
|
+
const DEFAULT_REHYPE_PLUGINS: MdxRollupPluginOptions['rehypePlugins'] = [];
|
|
33
28
|
|
|
34
29
|
function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] = []): T[] {
|
|
35
30
|
if (Array.isArray(config)) return config;
|
|
@@ -37,44 +32,54 @@ function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] =
|
|
|
37
32
|
return [...defaults, ...(config?.extends ?? [])];
|
|
38
33
|
}
|
|
39
34
|
|
|
35
|
+
function getRemarkPlugins(
|
|
36
|
+
mdxOptions: MdxOptions,
|
|
37
|
+
config: AstroConfig
|
|
38
|
+
): MdxRollupPluginOptions['remarkPlugins'] {
|
|
39
|
+
let remarkPlugins = [
|
|
40
|
+
// Initialize vfile.data.astroExports before all plugins are run
|
|
41
|
+
remarkInitializeAstroData,
|
|
42
|
+
...handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS),
|
|
43
|
+
];
|
|
44
|
+
if (config.markdown.syntaxHighlight === 'shiki') {
|
|
45
|
+
// Default export still requires ".default" chaining for some reason
|
|
46
|
+
// Workarounds tried:
|
|
47
|
+
// - "import * as remarkShikiTwoslash"
|
|
48
|
+
// - "import { default as remarkShikiTwoslash }"
|
|
49
|
+
const shikiTwoslash = (remarkShikiTwoslash as any).default ?? remarkShikiTwoslash;
|
|
50
|
+
remarkPlugins.push([shikiTwoslash, config.markdown.shikiConfig]);
|
|
51
|
+
}
|
|
52
|
+
if (config.markdown.syntaxHighlight === 'prism') {
|
|
53
|
+
remarkPlugins.push(remarkPrism);
|
|
54
|
+
}
|
|
55
|
+
return remarkPlugins;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getRehypePlugins(
|
|
59
|
+
mdxOptions: MdxOptions,
|
|
60
|
+
config: AstroConfig
|
|
61
|
+
): MdxRollupPluginOptions['rehypePlugins'] {
|
|
62
|
+
let rehypePlugins = handleExtends(mdxOptions.rehypePlugins, DEFAULT_REHYPE_PLUGINS);
|
|
63
|
+
|
|
64
|
+
if (config.markdown.syntaxHighlight === 'shiki' || config.markdown.syntaxHighlight === 'prism') {
|
|
65
|
+
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
66
|
+
}
|
|
67
|
+
// getHeadings() is guaranteed by TS, so we can't allow user to override
|
|
68
|
+
rehypePlugins.push(rehypeCollectHeadings);
|
|
69
|
+
|
|
70
|
+
return rehypePlugins;
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
|
|
41
74
|
return {
|
|
42
75
|
name: '@astrojs/mdx',
|
|
43
76
|
hooks: {
|
|
44
77
|
'astro:config:setup': ({ updateConfig, config, addPageExtension, command }: any) => {
|
|
45
78
|
addPageExtension('.mdx');
|
|
46
|
-
let remarkPlugins = handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS);
|
|
47
|
-
let rehypePlugins = handleExtends(mdxOptions.rehypePlugins, DEFAULT_REHYPE_PLUGINS);
|
|
48
|
-
|
|
49
|
-
if (config.markdown.syntaxHighlight === 'shiki') {
|
|
50
|
-
remarkPlugins.push([
|
|
51
|
-
// Default export still requires ".default" chaining for some reason
|
|
52
|
-
// Workarounds tried:
|
|
53
|
-
// - "import * as remarkShikiTwoslash"
|
|
54
|
-
// - "import { default as remarkShikiTwoslash }"
|
|
55
|
-
(remarkShikiTwoslash as any).default ?? remarkShikiTwoslash,
|
|
56
|
-
config.markdown.shikiConfig,
|
|
57
|
-
]);
|
|
58
|
-
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (config.markdown.syntaxHighlight === 'prism') {
|
|
62
|
-
remarkPlugins.push(remarkPrism);
|
|
63
|
-
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
remarkPlugins.push(remarkFrontmatter);
|
|
67
|
-
remarkPlugins.push([
|
|
68
|
-
remarkMdxFrontmatter,
|
|
69
|
-
{
|
|
70
|
-
name: 'frontmatter',
|
|
71
|
-
...mdxOptions.frontmatterOptions,
|
|
72
|
-
},
|
|
73
|
-
]);
|
|
74
79
|
|
|
75
80
|
const mdxPluginOpts: MdxRollupPluginOptions = {
|
|
76
|
-
remarkPlugins,
|
|
77
|
-
rehypePlugins,
|
|
81
|
+
remarkPlugins: getRemarkPlugins(mdxOptions, config),
|
|
82
|
+
rehypePlugins: getRehypePlugins(mdxOptions, config),
|
|
78
83
|
jsx: true,
|
|
79
84
|
jsxImportSource: 'astro',
|
|
80
85
|
// Note: disable `.md` support
|
|
@@ -93,24 +98,23 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
|
|
|
93
98
|
async transform(code, id) {
|
|
94
99
|
if (!id.endsWith('mdx')) return;
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)})).default;\nreturn <Layout content={${JSON.stringify(
|
|
105
|
-
content
|
|
106
|
-
)}}>{children}</Layout> }`;
|
|
107
|
-
}
|
|
101
|
+
let { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
|
|
102
|
+
if (frontmatter.layout) {
|
|
103
|
+
const { layout, ...contentProp } = frontmatter;
|
|
104
|
+
pageContent += `\n\nexport default async function({ children }) {\nconst Layout = (await import(${JSON.stringify(
|
|
105
|
+
frontmatter.layout
|
|
106
|
+
)})).default;\nconst frontmatter=${JSON.stringify(
|
|
107
|
+
contentProp
|
|
108
|
+
)};\nreturn <Layout frontmatter={frontmatter} content={frontmatter} headings={getHeadings()}>{children}</Layout> }`;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
const compiled = await mdxCompile(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
|
|
112
|
+
...mdxPluginOpts,
|
|
113
|
+
rehypePlugins: [
|
|
114
|
+
...(mdxPluginOpts.rehypePlugins ?? []),
|
|
115
|
+
() => rehypeApplyFrontmatterExport(frontmatter),
|
|
116
|
+
],
|
|
117
|
+
});
|
|
114
118
|
|
|
115
119
|
return {
|
|
116
120
|
code: String(compiled.value),
|
package/src/utils.ts
CHANGED
|
@@ -47,9 +47,9 @@ export function getFileInfo(id: string, config: AstroConfig): FileInfo {
|
|
|
47
47
|
* Match YAML exception handling from Astro core errors
|
|
48
48
|
* @see 'astro/src/core/errors.ts'
|
|
49
49
|
*/
|
|
50
|
-
export function
|
|
50
|
+
export function parseFrontmatter(code: string, id: string) {
|
|
51
51
|
try {
|
|
52
|
-
return matter(code)
|
|
52
|
+
return matter(code);
|
|
53
53
|
} catch (e: any) {
|
|
54
54
|
if (e.name === 'YAMLException') {
|
|
55
55
|
const err: SSRError = e;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<p><slot /></p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<h1><slot/></h1>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import P from '../components/P.astro';
|
|
2
|
+
import Em from '../components/Em.astro';
|
|
3
|
+
import Title from '../components/Title.astro';
|
|
4
|
+
|
|
5
|
+
export const components = { p: P, em: Em, h1: Title };
|
|
6
|
+
|
|
7
|
+
# Hello _there_
|
|
8
|
+
|
|
9
|
+
# _there_
|
|
10
|
+
|
|
11
|
+
Hello _there_
|
|
12
|
+
|
|
13
|
+
_there_
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
---
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
content = { title: "content didn't work" },
|
|
4
|
+
frontmatter = { title: "frontmatter didn't work" },
|
|
5
|
+
headings = [],
|
|
6
|
+
} = Astro.props;
|
|
3
7
|
---
|
|
4
8
|
|
|
5
9
|
<!DOCTYPE html>
|
|
6
10
|
<html lang="en">
|
|
11
|
+
|
|
7
12
|
<head>
|
|
8
13
|
<meta charset="UTF-8">
|
|
9
14
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
10
15
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
11
|
-
<title>{content.title}</title>
|
|
12
16
|
</head>
|
|
17
|
+
|
|
13
18
|
<body>
|
|
14
|
-
<
|
|
19
|
+
<p data-content-title>{content.title}</p>
|
|
20
|
+
<p data-frontmatter-title>{frontmatter.title}</p>
|
|
15
21
|
<p data-layout-rendered>Layout rendered!</p>
|
|
22
|
+
<ul data-headings>
|
|
23
|
+
{headings.map(heading => <li>{heading.slug}</li>)}
|
|
24
|
+
</ul>
|
|
16
25
|
<slot />
|
|
17
26
|
</body>
|
|
27
|
+
|
|
18
28
|
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from 'astro/config';
|
|
2
|
+
import mdx from '@astrojs/mdx';
|
|
3
|
+
import { rehypeReadingTime, remarkTitle } from './src/markdown-plugins.mjs';
|
|
4
|
+
|
|
5
|
+
// https://astro.build/config
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
site: 'https://astro.build/',
|
|
8
|
+
integrations: [mdx({
|
|
9
|
+
remarkPlugins: [remarkTitle],
|
|
10
|
+
rehypePlugins: [rehypeReadingTime],
|
|
11
|
+
})],
|
|
12
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="$NODE_PATH:/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../../../../../../../astro/astro.js" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../../../../../../../astro/astro.js" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@test/mdx-frontmatter-injection",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"astro": "workspace:*",
|
|
7
|
+
"@astrojs/mdx": "workspace:*",
|
|
8
|
+
"mdast-util-to-string": "^3.1.0",
|
|
9
|
+
"reading-time": "^1.5.0",
|
|
10
|
+
"unist-util-visit": "^4.1.0"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import getReadingTime from 'reading-time';
|
|
2
|
+
import { toString } from 'mdast-util-to-string';
|
|
3
|
+
import { visit } from 'unist-util-visit';
|
|
4
|
+
|
|
5
|
+
export function rehypeReadingTime() {
|
|
6
|
+
return function (tree, { data }) {
|
|
7
|
+
const readingTime = getReadingTime(toString(tree));
|
|
8
|
+
data.astro.frontmatter.injectedReadingTime = readingTime;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function remarkTitle() {
|
|
13
|
+
return function (tree, { data }) {
|
|
14
|
+
visit(tree, ['heading'], (node) => {
|
|
15
|
+
if (node.depth === 1) {
|
|
16
|
+
data.astro.frontmatter.title = toString(node.children);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="$NODE_PATH:/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../../../../../../../astro/astro.js" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../../../../../../../astro/astro.js" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import mdx from '@astrojs/mdx';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { parseHTML } from 'linkedom';
|
|
5
|
+
import { loadFixture } from '../../../astro/test/test-utils.js';
|
|
6
|
+
|
|
7
|
+
const FIXTURE_ROOT = new URL('./fixtures/mdx-escape/', import.meta.url);
|
|
8
|
+
|
|
9
|
+
describe('MDX frontmatter', () => {
|
|
10
|
+
let fixture;
|
|
11
|
+
before(async () => {
|
|
12
|
+
fixture = await loadFixture({
|
|
13
|
+
root: FIXTURE_ROOT,
|
|
14
|
+
integrations: [mdx()],
|
|
15
|
+
});
|
|
16
|
+
await fixture.build();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('does not have unescaped HTML at top-level', async () => {
|
|
20
|
+
const html = await fixture.readFile('/index.html');
|
|
21
|
+
const { document } = parseHTML(html);
|
|
22
|
+
|
|
23
|
+
expect(document.body.textContent).to.not.include('<em');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('does not have unescaped HTML inside html tags', async () => {
|
|
27
|
+
const html = await fixture.readFile('/html-tag/index.html');
|
|
28
|
+
const { document } = parseHTML(html);
|
|
29
|
+
|
|
30
|
+
expect(document.body.textContent).to.not.include('<em');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { loadFixture } from '../../../astro/test/test-utils.js';
|
|
3
|
+
|
|
4
|
+
const FIXTURE_ROOT = new URL('./fixtures/mdx-frontmatter-injection/', import.meta.url);
|
|
5
|
+
|
|
6
|
+
describe('MDX frontmatter injection', () => {
|
|
7
|
+
let fixture;
|
|
8
|
+
|
|
9
|
+
before(async () => {
|
|
10
|
+
fixture = await loadFixture({
|
|
11
|
+
root: FIXTURE_ROOT,
|
|
12
|
+
});
|
|
13
|
+
await fixture.build();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('remark supports custom vfile data - get title', async () => {
|
|
17
|
+
const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
|
|
18
|
+
const titles = frontmatterByPage.map((frontmatter = {}) => frontmatter.title);
|
|
19
|
+
expect(titles).to.contain('Page 1');
|
|
20
|
+
expect(titles).to.contain('Page 2');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('rehype supports custom vfile data - reading time', async () => {
|
|
24
|
+
const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
|
|
25
|
+
const readingTimes = frontmatterByPage.map(
|
|
26
|
+
(frontmatter = {}) => frontmatter.injectedReadingTime
|
|
27
|
+
);
|
|
28
|
+
expect(readingTimes.length).to.be.greaterThan(0);
|
|
29
|
+
for (let readingTime of readingTimes) {
|
|
30
|
+
expect(readingTime).to.not.be.null;
|
|
31
|
+
expect(readingTime.text).match(/^\d+ min read/);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('overrides injected frontmatter with user frontmatter', async () => {
|
|
36
|
+
const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
|
|
37
|
+
const readingTimes = frontmatterByPage.map(
|
|
38
|
+
(frontmatter = {}) => frontmatter.injectedReadingTime?.text
|
|
39
|
+
);
|
|
40
|
+
const titles = frontmatterByPage.map((frontmatter = {}) => frontmatter.title);
|
|
41
|
+
expect(titles).to.contain('Overridden title');
|
|
42
|
+
expect(readingTimes).to.contain('1000 min read');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -7,33 +7,24 @@ import { loadFixture } from '../../../astro/test/test-utils.js';
|
|
|
7
7
|
const FIXTURE_ROOT = new URL('./fixtures/mdx-frontmatter/', import.meta.url);
|
|
8
8
|
|
|
9
9
|
describe('MDX frontmatter', () => {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
let fixture;
|
|
11
|
+
before(async () => {
|
|
12
|
+
fixture = await loadFixture({
|
|
12
13
|
root: FIXTURE_ROOT,
|
|
13
14
|
integrations: [mdx()],
|
|
14
15
|
});
|
|
15
16
|
await fixture.build();
|
|
17
|
+
});
|
|
18
|
+
it('builds when "frontmatter.property" is in JSX expression', async () => {
|
|
16
19
|
expect(true).to.equal(true);
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
it('extracts frontmatter to "frontmatter" export', async () => {
|
|
20
|
-
const fixture = await loadFixture({
|
|
21
|
-
root: FIXTURE_ROOT,
|
|
22
|
-
integrations: [mdx()],
|
|
23
|
-
});
|
|
24
|
-
await fixture.build();
|
|
25
|
-
|
|
26
23
|
const { titles } = JSON.parse(await fixture.readFile('/glob.json'));
|
|
27
24
|
expect(titles).to.include('Using YAML frontmatter');
|
|
28
25
|
});
|
|
29
26
|
|
|
30
27
|
it('renders layout from "layout" frontmatter property', async () => {
|
|
31
|
-
const fixture = await loadFixture({
|
|
32
|
-
root: FIXTURE_ROOT,
|
|
33
|
-
integrations: [mdx()],
|
|
34
|
-
});
|
|
35
|
-
await fixture.build();
|
|
36
|
-
|
|
37
28
|
const html = await fixture.readFile('/index.html');
|
|
38
29
|
const { document } = parseHTML(html);
|
|
39
30
|
|
|
@@ -42,35 +33,27 @@ describe('MDX frontmatter', () => {
|
|
|
42
33
|
expect(layoutParagraph).to.not.be.null;
|
|
43
34
|
});
|
|
44
35
|
|
|
45
|
-
it('passes frontmatter to layout via "content"
|
|
46
|
-
const fixture = await loadFixture({
|
|
47
|
-
root: FIXTURE_ROOT,
|
|
48
|
-
integrations: [mdx()],
|
|
49
|
-
});
|
|
50
|
-
await fixture.build();
|
|
51
|
-
|
|
36
|
+
it('passes frontmatter to layout via "content" and "frontmatter" props', async () => {
|
|
52
37
|
const html = await fixture.readFile('/index.html');
|
|
53
38
|
const { document } = parseHTML(html);
|
|
54
39
|
|
|
55
|
-
const
|
|
40
|
+
const contentTitle = document.querySelector('[data-content-title]');
|
|
41
|
+
const frontmatterTitle = document.querySelector('[data-frontmatter-title]');
|
|
56
42
|
|
|
57
|
-
expect(
|
|
43
|
+
expect(contentTitle.textContent).to.equal('Using YAML frontmatter');
|
|
44
|
+
expect(frontmatterTitle.textContent).to.equal('Using YAML frontmatter');
|
|
58
45
|
});
|
|
59
46
|
|
|
60
|
-
it('
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
integrations: [
|
|
64
|
-
mdx({
|
|
65
|
-
frontmatterOptions: {
|
|
66
|
-
name: 'customFrontmatter',
|
|
67
|
-
},
|
|
68
|
-
}),
|
|
69
|
-
],
|
|
70
|
-
});
|
|
71
|
-
await fixture.build();
|
|
47
|
+
it('passes headings to layout via "headings" prop', async () => {
|
|
48
|
+
const html = await fixture.readFile('/with-headings/index.html');
|
|
49
|
+
const { document } = parseHTML(html);
|
|
72
50
|
|
|
73
|
-
const
|
|
74
|
-
|
|
51
|
+
const headingSlugs = [...document.querySelectorAll('[data-headings] > li')].map(
|
|
52
|
+
(el) => el.textContent
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(headingSlugs.length).to.be.greaterThan(0);
|
|
56
|
+
expect(headingSlugs).to.contain('section-1');
|
|
57
|
+
expect(headingSlugs).to.contain('section-2');
|
|
75
58
|
});
|
|
76
59
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import mdx from '@astrojs/mdx';
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { parseHTML } from 'linkedom';
|
|
5
|
+
import { loadFixture } from '../../../astro/test/test-utils.js';
|
|
6
|
+
|
|
7
|
+
describe('MDX and React', () => {
|
|
8
|
+
let fixture;
|
|
9
|
+
|
|
10
|
+
before(async () => {
|
|
11
|
+
fixture = await loadFixture({
|
|
12
|
+
root: new URL('./fixtures/mdx-plus-react/', import.meta.url),
|
|
13
|
+
});
|
|
14
|
+
await fixture.build();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('can be used in the same project', async () => {
|
|
18
|
+
const html = await fixture.readFile('/index.html');
|
|
19
|
+
const { document } = parseHTML(html);
|
|
20
|
+
|
|
21
|
+
const p = document.querySelector('p');
|
|
22
|
+
|
|
23
|
+
expect(p.textContent).to.equal('Hello world');
|
|
24
|
+
});
|
|
25
|
+
});
|