@astrojs/mdx 0.4.0 → 0.7.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 +37 -0
- package/README.md +21 -3
- package/dist/astro-data-utils.d.ts +9 -0
- package/dist/astro-data-utils.js +66 -0
- package/dist/index.js +64 -47
- package/dist/rehype-collect-headings.d.ts +6 -0
- package/dist/rehype-collect-headings.js +47 -0
- package/dist/remark-prism.js +2 -34
- package/dist/utils.d.ts +5 -3
- package/dist/utils.js +22 -3
- package/package.json +9 -6
- package/src/astro-data-utils.ts +81 -0
- package/src/index.ts +83 -63
- package/src/rehype-collect-headings.ts +50 -0
- package/src/remark-prism.ts +2 -44
- package/src/utils.ts +27 -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-get-headings/src/pages/pages.json.js +11 -0
- package/test/fixtures/mdx-get-headings/src/pages/test-with-jsx-expressions.mdx +8 -0
- package/test/fixtures/mdx-get-headings/src/pages/test.mdx +9 -0
- package/test/fixtures/mdx-page/astro.config.ts +5 -0
- package/test/fixtures/mdx-page/node_modules/.bin/astro +17 -0
- package/test/fixtures/mdx-page/package.json +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 +7 -0
- package/test/fixtures/mdx-rehype-plugins/src/pages/space-ipsum.mdx +25 -0
- 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 +26 -26
- package/test/mdx-get-headings.test.js +60 -0
- package/test/mdx-page.test.js +0 -1
- package/test/mdx-plus-react.test.js +25 -0
- package/test/mdx-rehype-plugins.test.js +70 -0
package/src/index.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { nodeTypes } from '@mdx-js/mdx';
|
|
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
7
|
import type { RemarkMdxFrontmatterOptions } from 'remark-mdx-frontmatter';
|
|
9
|
-
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
|
|
10
8
|
import remarkShikiTwoslash from 'remark-shiki-twoslash';
|
|
11
9
|
import remarkSmartypants from 'remark-smartypants';
|
|
10
|
+
import { VFile } from 'vfile';
|
|
12
11
|
import type { Plugin as VitePlugin } from 'vite';
|
|
12
|
+
import { rehypeApplyFrontmatterExport, remarkInitializeAstroData } from './astro-data-utils.js';
|
|
13
|
+
import rehypeCollectHeadings from './rehype-collect-headings.js';
|
|
13
14
|
import remarkPrism from './remark-prism.js';
|
|
14
|
-
import { getFileInfo,
|
|
15
|
+
import { getFileInfo, parseFrontmatter } from './utils.js';
|
|
15
16
|
|
|
16
17
|
type WithExtends<T> = T | { extends: T };
|
|
17
18
|
|
|
@@ -26,7 +27,11 @@ type MdxOptions = {
|
|
|
26
27
|
frontmatterOptions?: RemarkMdxFrontmatterOptions;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
const DEFAULT_REMARK_PLUGINS = [
|
|
30
|
+
const DEFAULT_REMARK_PLUGINS: MdxRollupPluginOptions['remarkPlugins'] = [
|
|
31
|
+
remarkGfm,
|
|
32
|
+
remarkSmartypants,
|
|
33
|
+
];
|
|
34
|
+
const DEFAULT_REHYPE_PLUGINS: MdxRollupPluginOptions['rehypePlugins'] = [];
|
|
30
35
|
|
|
31
36
|
function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] = []): T[] {
|
|
32
37
|
if (Array.isArray(config)) return config;
|
|
@@ -34,92 +39,107 @@ function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] =
|
|
|
34
39
|
return [...defaults, ...(config?.extends ?? [])];
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
function getRemarkPlugins(
|
|
43
|
+
mdxOptions: MdxOptions,
|
|
44
|
+
config: AstroConfig
|
|
45
|
+
): MdxRollupPluginOptions['remarkPlugins'] {
|
|
46
|
+
let remarkPlugins = [
|
|
47
|
+
// Initialize vfile.data.astroExports before all plugins are run
|
|
48
|
+
remarkInitializeAstroData,
|
|
49
|
+
...handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS),
|
|
50
|
+
];
|
|
51
|
+
if (config.markdown.syntaxHighlight === 'shiki') {
|
|
52
|
+
// Default export still requires ".default" chaining for some reason
|
|
53
|
+
// Workarounds tried:
|
|
54
|
+
// - "import * as remarkShikiTwoslash"
|
|
55
|
+
// - "import { default as remarkShikiTwoslash }"
|
|
56
|
+
const shikiTwoslash = (remarkShikiTwoslash as any).default ?? remarkShikiTwoslash;
|
|
57
|
+
remarkPlugins.push([shikiTwoslash, config.markdown.shikiConfig]);
|
|
58
|
+
}
|
|
59
|
+
if (config.markdown.syntaxHighlight === 'prism') {
|
|
60
|
+
remarkPlugins.push(remarkPrism);
|
|
61
|
+
}
|
|
62
|
+
return remarkPlugins;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getRehypePlugins(
|
|
66
|
+
mdxOptions: MdxOptions,
|
|
67
|
+
config: AstroConfig
|
|
68
|
+
): MdxRollupPluginOptions['rehypePlugins'] {
|
|
69
|
+
let rehypePlugins = handleExtends(mdxOptions.rehypePlugins, DEFAULT_REHYPE_PLUGINS);
|
|
70
|
+
|
|
71
|
+
if (config.markdown.syntaxHighlight === 'shiki' || config.markdown.syntaxHighlight === 'prism') {
|
|
72
|
+
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
73
|
+
}
|
|
74
|
+
// getHeadings() is guaranteed by TS, so we can't allow user to override
|
|
75
|
+
rehypePlugins.push(rehypeCollectHeadings);
|
|
76
|
+
|
|
77
|
+
return rehypePlugins;
|
|
78
|
+
}
|
|
79
|
+
|
|
37
80
|
export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
|
|
38
81
|
return {
|
|
39
82
|
name: '@astrojs/mdx',
|
|
40
83
|
hooks: {
|
|
41
84
|
'astro:config:setup': ({ updateConfig, config, addPageExtension, command }: any) => {
|
|
42
85
|
addPageExtension('.mdx');
|
|
43
|
-
let remarkPlugins = handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS);
|
|
44
|
-
let rehypePlugins = handleExtends(mdxOptions.rehypePlugins);
|
|
45
|
-
|
|
46
|
-
if (config.markdown.syntaxHighlight === 'shiki') {
|
|
47
|
-
remarkPlugins.push([
|
|
48
|
-
// Default export still requires ".default" chaining for some reason
|
|
49
|
-
// Workarounds tried:
|
|
50
|
-
// - "import * as remarkShikiTwoslash"
|
|
51
|
-
// - "import { default as remarkShikiTwoslash }"
|
|
52
|
-
(remarkShikiTwoslash as any).default,
|
|
53
|
-
config.markdown.shikiConfig,
|
|
54
|
-
]);
|
|
55
|
-
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (config.markdown.syntaxHighlight === 'prism') {
|
|
59
|
-
remarkPlugins.push(remarkPrism);
|
|
60
|
-
rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
remarkPlugins.push(remarkFrontmatter);
|
|
64
|
-
remarkPlugins.push([
|
|
65
|
-
remarkMdxFrontmatter,
|
|
66
|
-
{
|
|
67
|
-
name: 'frontmatter',
|
|
68
|
-
...mdxOptions.frontmatterOptions,
|
|
69
|
-
},
|
|
70
|
-
]);
|
|
71
86
|
|
|
72
|
-
const
|
|
73
|
-
remarkPlugins,
|
|
74
|
-
rehypePlugins,
|
|
87
|
+
const mdxPluginOpts: MdxRollupPluginOptions = {
|
|
88
|
+
remarkPlugins: getRemarkPlugins(mdxOptions, config),
|
|
89
|
+
rehypePlugins: getRehypePlugins(mdxOptions, config),
|
|
75
90
|
jsx: true,
|
|
76
91
|
jsxImportSource: 'astro',
|
|
77
92
|
// Note: disable `.md` support
|
|
78
93
|
format: 'mdx',
|
|
79
94
|
mdExtensions: [],
|
|
80
|
-
}
|
|
95
|
+
};
|
|
81
96
|
|
|
82
97
|
updateConfig({
|
|
83
98
|
vite: {
|
|
84
99
|
plugins: [
|
|
85
100
|
{
|
|
86
101
|
enforce: 'pre',
|
|
87
|
-
...
|
|
88
|
-
// Override transform to
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
...mdxPlugin(mdxPluginOpts),
|
|
103
|
+
// Override transform to alter code before MDX compilation
|
|
104
|
+
// ex. inject layouts
|
|
105
|
+
async transform(code, id) {
|
|
106
|
+
if (!id.endsWith('mdx')) return;
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
// If user overrides our default YAML parser,
|
|
94
|
-
// do not attempt to parse the `layout` via gray-matter
|
|
95
|
-
if (mdxOptions.frontmatterOptions?.parsers) {
|
|
96
|
-
return mdxPluginTransform?.(code, id);
|
|
97
|
-
}
|
|
98
|
-
const frontmatter = getFrontmatter(code, id);
|
|
108
|
+
let { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
|
|
99
109
|
if (frontmatter.layout) {
|
|
100
|
-
const { layout, ...
|
|
101
|
-
|
|
110
|
+
const { layout, ...contentProp } = frontmatter;
|
|
111
|
+
pageContent += `\n\nexport default async function({ children }) {\nconst Layout = (await import(${JSON.stringify(
|
|
102
112
|
frontmatter.layout
|
|
103
|
-
)})).default;\
|
|
104
|
-
|
|
105
|
-
)}}>{children}</Layout> }`;
|
|
113
|
+
)})).default;\nconst frontmatter=${JSON.stringify(
|
|
114
|
+
contentProp
|
|
115
|
+
)};\nreturn <Layout frontmatter={frontmatter} content={frontmatter} headings={getHeadings()}>{children}</Layout> }`;
|
|
106
116
|
}
|
|
107
|
-
|
|
117
|
+
|
|
118
|
+
const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
|
|
119
|
+
...mdxPluginOpts,
|
|
120
|
+
rehypePlugins: [
|
|
121
|
+
...(mdxPluginOpts.rehypePlugins ?? []),
|
|
122
|
+
() =>
|
|
123
|
+
rehypeApplyFrontmatterExport(
|
|
124
|
+
frontmatter,
|
|
125
|
+
mdxOptions.frontmatterOptions?.name
|
|
126
|
+
),
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
code: String(compiled.value),
|
|
132
|
+
map: compiled.map,
|
|
133
|
+
};
|
|
108
134
|
},
|
|
109
135
|
},
|
|
110
136
|
{
|
|
111
|
-
name: '@astrojs/mdx',
|
|
137
|
+
name: '@astrojs/mdx-postprocess',
|
|
138
|
+
// These transforms must happen *after* JSX runtime transformations
|
|
112
139
|
transform(code, id) {
|
|
113
140
|
if (!id.endsWith('.mdx')) return;
|
|
114
141
|
const [, moduleExports] = parseESM(code);
|
|
115
142
|
|
|
116
|
-
// This adds support for injected "page-ssr" scripts in MDX files.
|
|
117
|
-
// TODO: This should only be happening on page entrypoints, not all imported MDX.
|
|
118
|
-
// TODO: This code is copy-pasted across all Astro/Vite plugins that deal with page
|
|
119
|
-
// entrypoints (.astro, .md, .mdx). This should be handled in some centralized place,
|
|
120
|
-
// or otherwise refactored to not require copy-paste handling logic.
|
|
121
|
-
code += `\nimport "${'astro:scripts/page-ssr.js'}";`;
|
|
122
|
-
|
|
123
143
|
const { fileUrl, fileId } = getFileInfo(id, config);
|
|
124
144
|
if (!moduleExports.includes('url')) {
|
|
125
145
|
code += `\nexport const url = ${JSON.stringify(fileUrl)};`;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import Slugger from 'github-slugger';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { jsToTreeNode } from './utils.js';
|
|
4
|
+
|
|
5
|
+
export interface MarkdownHeading {
|
|
6
|
+
depth: number;
|
|
7
|
+
slug: string;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function rehypeCollectHeadings() {
|
|
12
|
+
const slugger = new Slugger();
|
|
13
|
+
return function (tree: any) {
|
|
14
|
+
const headings: MarkdownHeading[] = [];
|
|
15
|
+
visit(tree, (node) => {
|
|
16
|
+
if (node.type !== 'element') return;
|
|
17
|
+
const { tagName } = node;
|
|
18
|
+
if (tagName[0] !== 'h') return;
|
|
19
|
+
const [_, level] = tagName.match(/h([0-6])/) ?? [];
|
|
20
|
+
if (!level) return;
|
|
21
|
+
const depth = Number.parseInt(level);
|
|
22
|
+
|
|
23
|
+
let text = '';
|
|
24
|
+
visit(node, (child, __, parent) => {
|
|
25
|
+
if (child.type === 'element' || parent == null) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (child.type === 'raw' && child.value.match(/^\n?<.*>\n?$/)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (new Set(['text', 'raw', 'mdxTextExpression']).has(child.type)) {
|
|
32
|
+
text += child.value;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
node.properties = node.properties || {};
|
|
37
|
+
if (typeof node.properties.id !== 'string') {
|
|
38
|
+
let slug = slugger.slug(text);
|
|
39
|
+
if (slug.endsWith('-')) {
|
|
40
|
+
slug = slug.slice(0, -1);
|
|
41
|
+
}
|
|
42
|
+
node.properties.id = slug;
|
|
43
|
+
}
|
|
44
|
+
headings.push({ depth, slug: node.properties.id, text });
|
|
45
|
+
});
|
|
46
|
+
tree.children.unshift(
|
|
47
|
+
jsToTreeNode(`export function getHeadings() { return ${JSON.stringify(headings)} }`)
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
}
|
package/src/remark-prism.ts
CHANGED
|
@@ -1,48 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { addAstro } from '@astrojs/prism/internal';
|
|
3
|
-
import Prism from 'prismjs';
|
|
4
|
-
import loadLanguages from 'prismjs/components/index.js';
|
|
1
|
+
import { runHighlighterWithAstro } from '@astrojs/prism/dist/highlighter';
|
|
5
2
|
import { visit } from 'unist-util-visit';
|
|
6
3
|
|
|
7
|
-
const languageMap = new Map([['ts', 'typescript']]);
|
|
8
|
-
|
|
9
|
-
function runHighlighter(lang: string, code: string) {
|
|
10
|
-
let classLanguage = `language-${lang}`;
|
|
11
|
-
|
|
12
|
-
if (lang == null) {
|
|
13
|
-
lang = 'plaintext';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const ensureLoaded = (language: string) => {
|
|
17
|
-
if (language && !Prism.languages[language]) {
|
|
18
|
-
loadLanguages([language]);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
if (languageMap.has(lang)) {
|
|
23
|
-
ensureLoaded(languageMap.get(lang)!);
|
|
24
|
-
} else if (lang === 'astro') {
|
|
25
|
-
ensureLoaded('typescript');
|
|
26
|
-
addAstro(Prism);
|
|
27
|
-
} else {
|
|
28
|
-
ensureLoaded('markup-templating'); // Prism expects this to exist for a number of other langs
|
|
29
|
-
ensureLoaded(lang);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (lang && !Prism.languages[lang]) {
|
|
33
|
-
// eslint-disable-next-line no-console
|
|
34
|
-
console.warn(`Unable to load the language: ${lang}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const grammar = Prism.languages[lang];
|
|
38
|
-
let html = code;
|
|
39
|
-
if (grammar) {
|
|
40
|
-
html = Prism.highlight(code, grammar, lang);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { classLanguage, html };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
4
|
/** */
|
|
47
5
|
export default function remarkPrism() {
|
|
48
6
|
return (tree: any) =>
|
|
@@ -50,7 +8,7 @@ export default function remarkPrism() {
|
|
|
50
8
|
let { lang, value } = node;
|
|
51
9
|
node.type = 'html';
|
|
52
10
|
|
|
53
|
-
let { html, classLanguage } =
|
|
11
|
+
let { html, classLanguage } = runHighlighterWithAstro(lang, value);
|
|
54
12
|
let classes = [classLanguage];
|
|
55
13
|
node.value = `<pre class="${classes.join(
|
|
56
14
|
' '
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { Options as AcornOpts } from 'acorn';
|
|
2
|
+
import { parse } from 'acorn';
|
|
1
3
|
import type { AstroConfig, SSRError } from 'astro';
|
|
4
|
+
import type { MdxjsEsm } from 'mdast-util-mdx';
|
|
5
|
+
|
|
2
6
|
import matter from 'gray-matter';
|
|
3
7
|
|
|
4
8
|
function appendForwardSlash(path: string) {
|
|
@@ -43,9 +47,9 @@ export function getFileInfo(id: string, config: AstroConfig): FileInfo {
|
|
|
43
47
|
* Match YAML exception handling from Astro core errors
|
|
44
48
|
* @see 'astro/src/core/errors.ts'
|
|
45
49
|
*/
|
|
46
|
-
export function
|
|
50
|
+
export function parseFrontmatter(code: string, id: string) {
|
|
47
51
|
try {
|
|
48
|
-
return matter(code)
|
|
52
|
+
return matter(code);
|
|
49
53
|
} catch (e: any) {
|
|
50
54
|
if (e.name === 'YAMLException') {
|
|
51
55
|
const err: SSRError = e;
|
|
@@ -58,3 +62,24 @@ export function getFrontmatter(code: string, id: string) {
|
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
65
|
+
|
|
66
|
+
export function jsToTreeNode(
|
|
67
|
+
jsString: string,
|
|
68
|
+
acornOpts: AcornOpts = {
|
|
69
|
+
ecmaVersion: 'latest',
|
|
70
|
+
sourceType: 'module',
|
|
71
|
+
}
|
|
72
|
+
): MdxjsEsm {
|
|
73
|
+
return {
|
|
74
|
+
type: 'mdxjsEsm',
|
|
75
|
+
value: '',
|
|
76
|
+
data: {
|
|
77
|
+
estree: {
|
|
78
|
+
body: [],
|
|
79
|
+
...parse(jsString, acornOpts),
|
|
80
|
+
type: 'Program',
|
|
81
|
+
sourceType: 'module',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -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,11 @@
|
|
|
1
|
+
export async function get() {
|
|
2
|
+
const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
body: JSON.stringify({
|
|
6
|
+
headingsByPage: Object.fromEntries(
|
|
7
|
+
Object.entries(mdxPages ?? {}).map(([k, v]) => [k, v?.getHeadings()])
|
|
8
|
+
),
|
|
9
|
+
}),
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -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,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
|