@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.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +21 -3
  4. package/dist/astro-data-utils.d.ts +9 -0
  5. package/dist/astro-data-utils.js +66 -0
  6. package/dist/index.js +64 -47
  7. package/dist/rehype-collect-headings.d.ts +6 -0
  8. package/dist/rehype-collect-headings.js +47 -0
  9. package/dist/remark-prism.js +2 -34
  10. package/dist/utils.d.ts +5 -3
  11. package/dist/utils.js +22 -3
  12. package/package.json +9 -6
  13. package/src/astro-data-utils.ts +81 -0
  14. package/src/index.ts +83 -63
  15. package/src/rehype-collect-headings.ts +50 -0
  16. package/src/remark-prism.ts +2 -44
  17. package/src/utils.ts +27 -2
  18. package/test/fixtures/mdx-escape/src/components/Em.astro +7 -0
  19. package/test/fixtures/mdx-escape/src/components/P.astro +1 -0
  20. package/test/fixtures/mdx-escape/src/components/Title.astro +1 -0
  21. package/test/fixtures/mdx-escape/src/pages/html-tag.mdx +5 -0
  22. package/test/fixtures/mdx-escape/src/pages/index.mdx +13 -0
  23. package/test/fixtures/mdx-frontmatter/src/layouts/Base.astro +13 -3
  24. package/test/fixtures/mdx-frontmatter/src/pages/index.mdx +3 -0
  25. package/test/fixtures/mdx-frontmatter/src/pages/with-headings.mdx +7 -0
  26. package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +12 -0
  27. package/test/fixtures/mdx-frontmatter-injection/node_modules/.bin/astro +17 -0
  28. package/test/fixtures/mdx-frontmatter-injection/package.json +12 -0
  29. package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +20 -0
  30. package/test/fixtures/mdx-frontmatter-injection/src/pages/glob.json.js +6 -0
  31. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +3 -0
  32. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +19 -0
  33. package/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx +7 -0
  34. package/test/fixtures/mdx-get-headings/src/pages/pages.json.js +11 -0
  35. package/test/fixtures/mdx-get-headings/src/pages/test-with-jsx-expressions.mdx +8 -0
  36. package/test/fixtures/mdx-get-headings/src/pages/test.mdx +9 -0
  37. package/test/fixtures/mdx-page/astro.config.ts +5 -0
  38. package/test/fixtures/mdx-page/node_modules/.bin/astro +17 -0
  39. package/test/fixtures/mdx-page/package.json +7 -0
  40. package/test/fixtures/mdx-plus-react/astro.config.mjs +6 -0
  41. package/test/fixtures/mdx-plus-react/node_modules/.bin/astro +17 -0
  42. package/test/fixtures/mdx-plus-react/package.json +8 -0
  43. package/test/fixtures/mdx-plus-react/src/components/Component.jsx +5 -0
  44. package/test/fixtures/mdx-plus-react/src/pages/index.astro +11 -0
  45. package/test/fixtures/mdx-rehype-plugins/src/pages/reading-time.json.js +7 -0
  46. package/test/fixtures/mdx-rehype-plugins/src/pages/space-ipsum.mdx +25 -0
  47. package/test/fixtures/mdx-remark-plugins/src/pages/headings-glob.json.js +6 -0
  48. package/test/mdx-escape.test.js +32 -0
  49. package/test/mdx-frontmatter-injection.test.js +44 -0
  50. package/test/mdx-frontmatter.test.js +26 -26
  51. package/test/mdx-get-headings.test.js +60 -0
  52. package/test/mdx-page.test.js +0 -1
  53. package/test/mdx-plus-react.test.js +25 -0
  54. 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, getFrontmatter } from './utils.js';
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 = [remarkGfm, remarkSmartypants];
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 configuredMdxPlugin = mdxPlugin({
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
- ...configuredMdxPlugin,
88
- // Override transform to inject layouts before MDX compilation
89
- async transform(this, code, id) {
90
- if (!id.endsWith('.mdx')) return;
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
- const mdxPluginTransform = configuredMdxPlugin.transform?.bind(this);
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, ...content } = frontmatter;
101
- code += `\nexport default async function({ children }) {\nconst Layout = (await import(${JSON.stringify(
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;\nreturn <Layout content={${JSON.stringify(
104
- content
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
- return mdxPluginTransform?.(code, id);
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
+ }
@@ -1,48 +1,6 @@
1
- // TODO: discuss extracting this file to @astrojs/prism
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 } = runHighlighter(lang, value);
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 getFrontmatter(code: string, id: string) {
50
+ export function parseFrontmatter(code: string, id: string) {
47
51
  try {
48
- return matter(code).data;
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,7 @@
1
+ <em><slot/></em>
2
+
3
+ <style>
4
+ em {
5
+ color: red;
6
+ }
7
+ </style>
@@ -0,0 +1 @@
1
+ <p><slot /></p>
@@ -0,0 +1 @@
1
+ <h1><slot/></h1>
@@ -0,0 +1,5 @@
1
+ import P from '../components/P.astro';
2
+ import Em from '../components/Em.astro';
3
+
4
+ <P>Render <Em>Me</Em></P>
5
+ <P><Em>Me</Em></P>
@@ -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 { content = { title: "Didn't work" } } = Astro.props;
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
- <h1>{content.title}</h1>
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>
@@ -5,3 +5,6 @@ illThrowIfIDontExist: "Oh no, that's scary!"
5
5
  ---
6
6
 
7
7
  {frontmatter.illThrowIfIDontExist}
8
+
9
+ > Note: newline intentionally missing from the end of this file.
10
+ > Useful since that can be the source of bugs in our compile step.
@@ -0,0 +1,7 @@
1
+ ---
2
+ layout: '../layouts/Base.astro'
3
+ ---
4
+
5
+ ## Section 1
6
+
7
+ ## Section 2
@@ -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,6 @@
1
+ export async function get() {
2
+ const docs = await import.meta.glob('./*.mdx', { eager: true });
3
+ return {
4
+ body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ # Page 1
2
+
3
+ Look at that!
@@ -0,0 +1,19 @@
1
+ # Page 2
2
+
3
+ ## Table of contents
4
+
5
+ ## Section 1
6
+
7
+ Some text!
8
+
9
+ ### Subsection 1
10
+
11
+ Some subsection test!
12
+
13
+ ### Subsection 2
14
+
15
+ Oh cool, more text!
16
+
17
+ ## Section 2
18
+
19
+ More content
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: 'Overridden title'
3
+ injectedReadingTime:
4
+ text: '1000 min read'
5
+ ---
6
+
7
+ # Working!
@@ -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,8 @@
1
+ export const h2Title = "Section 1"
2
+ export const h3Title = "Subsection 1"
3
+
4
+ # Heading test with JSX expressions
5
+
6
+ ## {h2Title}
7
+
8
+ ### {h3Title}
@@ -0,0 +1,9 @@
1
+ # Heading test
2
+
3
+ ## Section 1
4
+
5
+ ### Subsection 1
6
+
7
+ ### Subsection 2
8
+
9
+ ## Section 2
@@ -0,0 +1,5 @@
1
+ import mdx from '@astrojs/mdx';
2
+
3
+ export default {
4
+ integrations: [mdx()]
5
+ }
@@ -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,7 @@
1
+ {
2
+ "name": "@test/mdx-page",
3
+ "dependencies": {
4
+ "astro": "workspace:*",
5
+ "@astrojs/mdx": "workspace:*"
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ import mdx from '@astrojs/mdx';
2
+ import react from '@astrojs/react';
3
+
4
+ export default {
5
+ integrations: [react(), mdx()]
6
+ }
@@ -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,8 @@
1
+ {
2
+ "name": "@test/mdx-plus-react",
3
+ "dependencies": {
4
+ "astro": "workspace:*",
5
+ "@astrojs/mdx": "workspace:*",
6
+ "@astrojs/react": "workspace:*"
7
+ }
8
+ }
@@ -0,0 +1,5 @@
1
+ const Component = () => {
2
+ return <p>Hello world</p>;
3
+ };
4
+
5
+ export default Component;