@astrojs/mdx 0.14.0 → 0.15.0-beta.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 +2 -2
- package/CHANGELOG.md +105 -0
- package/README.md +70 -86
- package/dist/index.d.ts +8 -14
- package/dist/index.js +38 -18
- package/dist/plugins.d.ts +2 -3
- package/dist/plugins.js +25 -87
- package/package.json +4 -5
- package/src/index.ts +53 -30
- package/src/plugins.ts +28 -107
- package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +2 -2
- package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +7 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +1 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +1 -0
- package/test/mdx-frontmatter-injection.test.js +4 -7
- package/test/mdx-plugins.test.js +50 -84
- package/test/mdx-syntax-highlighting.test.js +26 -0
- package/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx +0 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
[35m@astrojs/mdx:build: [0mcache hit, replaying output [
|
|
1
|
+
[35m@astrojs/mdx:build: [0mcache hit, replaying output [2ma2df0a02dde16921[0m
|
|
2
2
|
[35m@astrojs/mdx:build: [0m
|
|
3
|
-
[35m@astrojs/mdx:build: [0m> @astrojs/mdx@0.
|
|
3
|
+
[35m@astrojs/mdx:build: [0m> @astrojs/mdx@0.15.0-beta.0 build /home/runner/work/astro/astro/packages/integrations/mdx
|
|
4
4
|
[35m@astrojs/mdx:build: [0m> astro-scripts build "src/**/*.ts" && tsc
|
|
5
5
|
[35m@astrojs/mdx:build: [0m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,110 @@
|
|
|
1
1
|
# @astrojs/mdx
|
|
2
2
|
|
|
3
|
+
## 0.15.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#5687](https://github.com/withastro/astro/pull/5687) [`e2019be6f`](https://github.com/withastro/astro/commit/e2019be6ffa46fa33d92cfd346f9ecbe51bb7144) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Give remark and rehype plugins access to user frontmatter via frontmatter injection. This means `data.astro.frontmatter` is now the _complete_ Markdown or MDX document's frontmatter, rather than an empty object.
|
|
8
|
+
|
|
9
|
+
This allows plugin authors to modify existing frontmatter, or compute new properties based on other properties. For example, say you want to compute a full image URL based on an `imageSrc` slug in your document frontmatter:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
export function remarkInjectSocialImagePlugin() {
|
|
13
|
+
return function (tree, file) {
|
|
14
|
+
const { frontmatter } = file.data.astro;
|
|
15
|
+
frontmatter.socialImageSrc = new URL(frontmatter.imageSrc, 'https://my-blog.com/').pathname;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
#### Content Collections - new `remarkPluginFrontmatter` property
|
|
21
|
+
|
|
22
|
+
We have changed _inject_ frontmatter to _modify_ frontmatter in our docs to improve discoverability. This is based on support forum feedback, where "injection" is rarely the term used.
|
|
23
|
+
|
|
24
|
+
To reflect this, the `injectedFrontmatter` property has been renamed to `remarkPluginFrontmatter`. This should clarify this plugin is still separate from the `data` export Content Collections expose today.
|
|
25
|
+
|
|
26
|
+
#### Migration instructions
|
|
27
|
+
|
|
28
|
+
Plugin authors should now **check for user frontmatter when applying defaults.**
|
|
29
|
+
|
|
30
|
+
For example, say a remark plugin wants to apply a default `title` if none is present. Add a conditional to check if the property is present, and update if none exists:
|
|
31
|
+
|
|
32
|
+
```diff
|
|
33
|
+
export function remarkInjectTitlePlugin() {
|
|
34
|
+
return function (tree, file) {
|
|
35
|
+
const { frontmatter } = file.data.astro;
|
|
36
|
+
+ if (!frontmatter.title) {
|
|
37
|
+
frontmatter.title = 'Default title';
|
|
38
|
+
+ }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This differs from previous behavior, where a Markdown file's frontmatter would _always_ override frontmatter injected via remark or reype.
|
|
44
|
+
|
|
45
|
+
- [#5684](https://github.com/withastro/astro/pull/5684) [`a9c292026`](https://github.com/withastro/astro/commit/a9c2920264e36cc5dc05f4adc1912187979edb0d) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Refine Markdown and MDX configuration options for ease-of-use.
|
|
46
|
+
|
|
47
|
+
#### Markdown
|
|
48
|
+
|
|
49
|
+
- **Remove `remark-smartypants`** from Astro's default Markdown plugins.
|
|
50
|
+
- **Replace the `extendDefaultPlugins` option** with a simplified `gfm` boolean. This is enabled by default, and can be disabled to remove GitHub-Flavored Markdown.
|
|
51
|
+
- Ensure GitHub-Flavored Markdown is applied whether or not custom `remarkPlugins` or `rehypePlugins` are configured. If you want to apply custom plugins _and_ remove GFM, manually set `gfm: false` in your config.
|
|
52
|
+
|
|
53
|
+
#### MDX
|
|
54
|
+
|
|
55
|
+
- Support _all_ Markdown configuration options (except `drafts`) from your MDX integration config. This includes `syntaxHighlighting` and `shikiConfig` options to further customize the MDX renderer.
|
|
56
|
+
- Simplify `extendDefaults` to an `extendMarkdownConfig` option. MDX options will default to their equivalent in your Markdown config. By setting `extendMarkdownConfig` to false, you can "eject" to set your own syntax highlighting, plugins, and more.
|
|
57
|
+
|
|
58
|
+
#### Migration
|
|
59
|
+
|
|
60
|
+
To preserve your existing Markdown and MDX setup, you may need some configuration changes:
|
|
61
|
+
|
|
62
|
+
##### Smartypants manual installation
|
|
63
|
+
|
|
64
|
+
[Smartypants](https://github.com/silvenon/remark-smartypants) has been removed from Astro's default setup. If you rely on this plugin, [install `remark-smartypants`](https://github.com/silvenon/remark-smartypants#installing) and apply to your `astro.config.*`:
|
|
65
|
+
|
|
66
|
+
```diff
|
|
67
|
+
// astro.config.mjs
|
|
68
|
+
import { defineConfig } from 'astro/config';
|
|
69
|
+
+ import smartypants from 'remark-smartypants';
|
|
70
|
+
|
|
71
|
+
export default defineConfig({
|
|
72
|
+
markdown: {
|
|
73
|
+
+ remarkPlugins: [smartypants],
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
##### Migrate `extendDefaultPlugins` to `gfm`
|
|
79
|
+
|
|
80
|
+
You may have disabled Astro's built-in plugins (GitHub-Flavored Markdown and Smartypants) with the `extendDefaultPlugins` option. Since Smartypants has been removed, this has been renamed to `gfm`.
|
|
81
|
+
|
|
82
|
+
```diff
|
|
83
|
+
// astro.config.mjs
|
|
84
|
+
import { defineConfig } from 'astro/config';
|
|
85
|
+
|
|
86
|
+
export default defineConfig({
|
|
87
|
+
markdown: {
|
|
88
|
+
- extendDefaultPlugins: false,
|
|
89
|
+
+ gfm: false,
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Additionally, applying remark and rehype plugins **no longer disables** `gfm`. You will need to opt-out manually by setting `gfm` to `false`.
|
|
95
|
+
|
|
96
|
+
##### Migrate MDX's `extendPlugins` to `extendMarkdownConfig`
|
|
97
|
+
|
|
98
|
+
You may have used the `extendPlugins` option to manage plugin defaults in MDX. This has been replaced by 2 flags:
|
|
99
|
+
|
|
100
|
+
- `extendMarkdownConfig` (`true` by default) to toggle Markdown config inheritance. This replaces the `extendPlugins: 'markdown'` option.
|
|
101
|
+
- `gfm` (`true` by default) to toggle GitHub-Flavored Markdown in MDX. This replaces the `extendPlugins: 'defaults'` option.
|
|
102
|
+
|
|
103
|
+
### Patch Changes
|
|
104
|
+
|
|
105
|
+
- Updated dependencies [[`e2019be6f`](https://github.com/withastro/astro/commit/e2019be6ffa46fa33d92cfd346f9ecbe51bb7144), [`a9c292026`](https://github.com/withastro/astro/commit/a9c2920264e36cc5dc05f4adc1912187979edb0d)]:
|
|
106
|
+
- @astrojs/markdown-remark@2.0.0-beta.0
|
|
107
|
+
|
|
3
108
|
## 0.14.0
|
|
4
109
|
|
|
5
110
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -78,116 +78,100 @@ Visit the [MDX docs](https://mdxjs.com/docs/what-is-mdx/) to learn about using s
|
|
|
78
78
|
|
|
79
79
|
Once the MDX integration is installed, no configuration is necessary to use `.mdx` files in your Astro project.
|
|
80
80
|
|
|
81
|
-
You can
|
|
81
|
+
You can configure how your MDX is rendered with the following options:
|
|
82
82
|
|
|
83
|
-
- [
|
|
84
|
-
- [`
|
|
85
|
-
- [`remarkPlugins`](#remarkplugins)
|
|
86
|
-
- [`rehypePlugins`](#rehypeplugins)
|
|
83
|
+
- [Options inherited from Markdown config](#options-inherited-from-markdown-config)
|
|
84
|
+
- [`extendMarkdownConfig`](#extendmarkdownconfig)
|
|
87
85
|
- [`recmaPlugins`](#recmaplugins)
|
|
88
86
|
|
|
89
|
-
###
|
|
87
|
+
### Options inherited from Markdown config
|
|
90
88
|
|
|
91
|
-
|
|
89
|
+
All [`markdown` configuration options](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) except `drafts` can be configured separately in the MDX integration. This includes remark and rehype plugins, syntax highlighting, and more. Options will default to those in your Markdown config ([see the `extendMarkdownConfig` option](#extendmarkdownconfig) to modify this).
|
|
92
90
|
|
|
93
|
-
|
|
91
|
+
:::note
|
|
92
|
+
There is no separate MDX configuration for [including pages marked as draft in the build](https://docs.astro.build/en/reference/configuration-reference/#markdowndrafts). This Markdown setting will be respected by both Markdown and MDX files and cannot be overriden for MDX files specifically.
|
|
93
|
+
:::
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Any additional plugins you apply in your MDX config will be applied *after* your configured Markdown plugins.
|
|
98
|
-
|
|
99
|
-
#### `astroDefaults`
|
|
100
|
-
|
|
101
|
-
Astro's MDX files will apply only [Astro's default plugins](/en/reference/configuration-reference/#markdownextenddefaultplugins), without inheriting the rest of your Markdown config.
|
|
102
|
-
|
|
103
|
-
This example will apply the default [GitHub-Flavored Markdown](https://github.com/remarkjs/remark-gfm) and [Smartypants](https://github.com/silvenon/remark-smartypants) plugins alongside [`remark-toc`](https://github.com/remarkjs/remark-toc) to your MDX files, while ignoring any `markdown.remarkPlugins` configuration:
|
|
104
|
-
|
|
105
|
-
```js "extendPlugins: 'astroDefaults'"
|
|
95
|
+
```ts
|
|
106
96
|
// astro.config.mjs
|
|
97
|
+
import { defineConfig } from 'astro/config';
|
|
98
|
+
import mdx from '@astrojs/mdx';
|
|
107
99
|
import remarkToc from 'remark-toc';
|
|
100
|
+
import rehypeMinifyHtml from 'rehype-minify-html';
|
|
108
101
|
|
|
109
|
-
export default {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
102
|
+
export default defineConfig({
|
|
103
|
+
integrations: [
|
|
104
|
+
mdx({
|
|
105
|
+
syntaxHighlight: 'shiki',
|
|
106
|
+
shikiConfig: { theme: 'dracula' },
|
|
107
|
+
remarkPlugins: [remarkToc],
|
|
108
|
+
rehypePlugins: [rehypeMinifyHtml],
|
|
109
|
+
remarkRehype: { footnoteLabel: 'Footnotes' },
|
|
110
|
+
gfm: false,
|
|
111
|
+
})
|
|
112
|
+
]
|
|
113
|
+
})
|
|
119
114
|
```
|
|
120
115
|
|
|
121
|
-
|
|
116
|
+
:::caution
|
|
117
|
+
MDX does not support passing remark and rehype plugins as a string. You should install, import, and apply the plugin function instead.
|
|
118
|
+
:::
|
|
122
119
|
|
|
123
|
-
|
|
120
|
+
📚 See the [Markdown Options reference](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) for a complete list of options.
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
// astro.config.mjs
|
|
127
|
-
import remarkToc from 'remark-toc';
|
|
128
|
-
|
|
129
|
-
export default {
|
|
130
|
-
integrations: [mdx({
|
|
131
|
-
remarkPlugins: [remarkToc],
|
|
132
|
-
// Astro defaults not applied
|
|
133
|
-
extendPlugins: false,
|
|
134
|
-
})],
|
|
135
|
-
}
|
|
136
|
-
```
|
|
122
|
+
### `extendMarkdownConfig`
|
|
137
123
|
|
|
138
|
-
|
|
124
|
+
- **Type:** `boolean`
|
|
125
|
+
- **Default:** `true`
|
|
139
126
|
|
|
140
|
-
|
|
127
|
+
MDX will extend [your project's existing Markdown configuration](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) by default. To override individual options, you can specify their equivalent in your MDX configuration.
|
|
141
128
|
|
|
142
|
-
|
|
129
|
+
For example, say you need to disable GitHub-Flavored Markdown and apply a different set of remark plugins for MDX files. You can apply these options like so, with `extendMarkdownConfig` enabled by default:
|
|
143
130
|
|
|
144
|
-
```
|
|
131
|
+
```ts
|
|
145
132
|
// astro.config.mjs
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
remarkRehype: {
|
|
149
|
-
footnoteLabel: 'Catatan kaki',
|
|
150
|
-
footnoteBackLabel: 'Kembali ke konten',
|
|
151
|
-
},
|
|
152
|
-
})],
|
|
153
|
-
};
|
|
154
|
-
```
|
|
155
|
-
This inherits the configuration of [`markdown.remarkRehype`](https://docs.astro.build/en/reference/configuration-reference/#markdownremarkrehype). This behavior can be changed by configuring `extendPlugins`.
|
|
156
|
-
|
|
157
|
-
### `remarkPlugins`
|
|
158
|
-
|
|
159
|
-
Browse [awesome-remark](https://github.com/remarkjs/awesome-remark) for a full curated list of [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) to extend your Markdown's capabilities.
|
|
160
|
-
|
|
161
|
-
This example applies the [`remark-toc`](https://github.com/remarkjs/remark-toc) plugin to `.mdx` files. To customize plugin inheritance from your Markdown config or Astro's defaults, [see the `extendPlugins` option](#extendplugins).
|
|
162
|
-
|
|
163
|
-
```js
|
|
164
|
-
// astro.config.mjs
|
|
165
|
-
import remarkToc from 'remark-toc';
|
|
133
|
+
import { defineConfig } from 'astro/config';
|
|
134
|
+
import mdx from '@astrojs/mdx';
|
|
166
135
|
|
|
167
|
-
export default {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
136
|
+
export default defineConfig({
|
|
137
|
+
markdown: {
|
|
138
|
+
syntaxHighlight: 'prism',
|
|
139
|
+
remarkPlugins: [remarkPlugin1],
|
|
140
|
+
gfm: true,
|
|
141
|
+
},
|
|
142
|
+
integrations: [
|
|
143
|
+
mdx({
|
|
144
|
+
// `syntaxHighlight` inherited from Markdown
|
|
145
|
+
|
|
146
|
+
// Markdown `remarkPlugins` ignored,
|
|
147
|
+
// only `remarkPlugin2` applied.
|
|
148
|
+
remarkPlugins: [remarkPlugin2],
|
|
149
|
+
// `gfm` overridden to `false`
|
|
150
|
+
gfm: false,
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
});
|
|
172
154
|
```
|
|
173
155
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
Browse [awesome-rehype](https://github.com/rehypejs/awesome-rehype) for a full curated list of [Rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) to transform the HTML that your Markdown generates.
|
|
156
|
+
You may also need to disable `markdown` config extension in MDX. For this, set `extendMarkdownConfig` to `false`:
|
|
177
157
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
This example applies the [`rehype-accessible-emojis`](https://www.npmjs.com/package/rehype-accessible-emojis) plugin to `.mdx` files. To customize plugin inheritance from your Markdown config or Astro's defaults, [see the `extendPlugins` option](#extendplugins).
|
|
181
|
-
|
|
182
|
-
```js
|
|
158
|
+
```ts
|
|
183
159
|
// astro.config.mjs
|
|
184
|
-
import
|
|
160
|
+
import { defineConfig } from 'astro/config';
|
|
161
|
+
import mdx from '@astrojs/mdx';
|
|
185
162
|
|
|
186
|
-
export default {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
163
|
+
export default defineConfig({
|
|
164
|
+
markdown: {
|
|
165
|
+
remarkPlugins: [remarkPlugin1],
|
|
166
|
+
},
|
|
167
|
+
integrations: [
|
|
168
|
+
mdx({
|
|
169
|
+
// Markdown config now ignored
|
|
170
|
+
extendMarkdownConfig: false,
|
|
171
|
+
// No `remarkPlugins` applied
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
});
|
|
191
175
|
```
|
|
192
176
|
|
|
193
177
|
### `recmaPlugins`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
+
import { markdownConfigDefaults } from '@astrojs/markdown-remark';
|
|
1
2
|
import { PluggableList } from '@mdx-js/mdx/lib/core.js';
|
|
2
3
|
import type { AstroIntegration } from 'astro';
|
|
3
4
|
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
|
|
4
|
-
export declare type MdxOptions = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
* - "markdown" (default) - inherit your project’s markdown plugin config ([see Markdown docs](https://docs.astro.build/en/guides/markdown-content/#configuring-markdown))
|
|
12
|
-
* - "astroDefaults" - inherit Astro’s default plugins only ([see defaults](https://docs.astro.build/en/reference/configuration-reference/#markdownextenddefaultplugins))
|
|
13
|
-
* - false - do not inherit any plugins
|
|
14
|
-
*/
|
|
15
|
-
extendPlugins?: 'markdown' | 'astroDefaults' | false;
|
|
16
|
-
remarkRehype?: RemarkRehypeOptions;
|
|
5
|
+
export declare type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
|
|
6
|
+
extendMarkdownConfig: boolean;
|
|
7
|
+
recmaPlugins: PluggableList;
|
|
8
|
+
remarkPlugins: PluggableList;
|
|
9
|
+
rehypePlugins: PluggableList;
|
|
10
|
+
remarkRehype: RemarkRehypeOptions;
|
|
17
11
|
};
|
|
18
|
-
export default function mdx(
|
|
12
|
+
export default function mdx(partialMdxOptions?: Partial<MdxOptions>): AstroIntegration;
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,34 @@
|
|
|
1
|
+
import { markdownConfigDefaults } from "@astrojs/markdown-remark";
|
|
2
|
+
import { toRemarkInitializeAstroData } from "@astrojs/markdown-remark/dist/internal.js";
|
|
1
3
|
import { compile as mdxCompile } from "@mdx-js/mdx";
|
|
2
4
|
import mdxPlugin from "@mdx-js/rollup";
|
|
3
5
|
import { parse as parseESM } from "es-module-lexer";
|
|
4
6
|
import fs from "node:fs/promises";
|
|
5
7
|
import { VFile } from "vfile";
|
|
6
|
-
import {
|
|
7
|
-
getRehypePlugins,
|
|
8
|
-
getRemarkPlugins,
|
|
9
|
-
recmaInjectImportMetaEnvPlugin,
|
|
10
|
-
rehypeApplyFrontmatterExport
|
|
11
|
-
} from "./plugins.js";
|
|
8
|
+
import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from "./plugins.js";
|
|
12
9
|
import { getFileInfo, parseFrontmatter } from "./utils.js";
|
|
13
10
|
const RAW_CONTENT_ERROR = "MDX does not support rawContent()! If you need to read the Markdown contents to calculate values (ex. reading time), we suggest injecting frontmatter via remark plugins. Learn more on our docs: https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins";
|
|
14
11
|
const COMPILED_CONTENT_ERROR = "MDX does not support compiledContent()! If you need to read the HTML contents to calculate values (ex. reading time), we suggest injecting frontmatter via rehype plugins. Learn more on our docs: https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins";
|
|
15
|
-
function mdx(
|
|
12
|
+
function mdx(partialMdxOptions = {}) {
|
|
16
13
|
return {
|
|
17
14
|
name: "@astrojs/mdx",
|
|
18
15
|
hooks: {
|
|
19
16
|
"astro:config:setup": async ({ updateConfig, config, addPageExtension, command }) => {
|
|
20
17
|
addPageExtension(".mdx");
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
18
|
+
const extendMarkdownConfig = partialMdxOptions.extendMarkdownConfig ?? defaultOptions.extendMarkdownConfig;
|
|
19
|
+
const mdxOptions = applyDefaultOptions({
|
|
20
|
+
options: partialMdxOptions,
|
|
21
|
+
defaults: extendMarkdownConfig ? config.markdown : defaultOptions
|
|
22
|
+
});
|
|
26
23
|
const mdxPluginOpts = {
|
|
27
24
|
remarkPlugins: await getRemarkPlugins(mdxOptions, config),
|
|
28
|
-
rehypePlugins: getRehypePlugins(mdxOptions
|
|
25
|
+
rehypePlugins: getRehypePlugins(mdxOptions),
|
|
29
26
|
recmaPlugins: mdxOptions.recmaPlugins,
|
|
27
|
+
remarkRehypeOptions: mdxOptions.remarkRehype,
|
|
30
28
|
jsx: true,
|
|
31
29
|
jsxImportSource: "astro",
|
|
32
30
|
format: "mdx",
|
|
33
|
-
mdExtensions: []
|
|
34
|
-
remarkRehypeOptions
|
|
31
|
+
mdExtensions: []
|
|
35
32
|
};
|
|
36
33
|
let importMetaEnv = {
|
|
37
34
|
SITE: config.site
|
|
@@ -53,9 +50,9 @@ function mdx(mdxOptions = {}) {
|
|
|
53
50
|
const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
|
|
54
51
|
const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
|
|
55
52
|
...mdxPluginOpts,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
remarkPlugins: [
|
|
54
|
+
toRemarkInitializeAstroData({ userFrontmatter: frontmatter }),
|
|
55
|
+
...mdxPluginOpts.remarkPlugins ?? []
|
|
59
56
|
],
|
|
60
57
|
recmaPlugins: [
|
|
61
58
|
...mdxPluginOpts.recmaPlugins ?? [],
|
|
@@ -129,6 +126,29 @@ if (import.meta.hot) {
|
|
|
129
126
|
}
|
|
130
127
|
};
|
|
131
128
|
}
|
|
129
|
+
const defaultOptions = {
|
|
130
|
+
...markdownConfigDefaults,
|
|
131
|
+
extendMarkdownConfig: true,
|
|
132
|
+
recmaPlugins: [],
|
|
133
|
+
remarkPlugins: [],
|
|
134
|
+
rehypePlugins: [],
|
|
135
|
+
remarkRehype: {}
|
|
136
|
+
};
|
|
137
|
+
function applyDefaultOptions({
|
|
138
|
+
options,
|
|
139
|
+
defaults
|
|
140
|
+
}) {
|
|
141
|
+
return {
|
|
142
|
+
syntaxHighlight: options.syntaxHighlight ?? defaults.syntaxHighlight,
|
|
143
|
+
extendMarkdownConfig: options.extendMarkdownConfig ?? defaults.extendMarkdownConfig,
|
|
144
|
+
recmaPlugins: options.recmaPlugins ?? defaults.recmaPlugins,
|
|
145
|
+
remarkRehype: options.remarkRehype ?? defaults.remarkRehype,
|
|
146
|
+
gfm: options.gfm ?? defaults.gfm,
|
|
147
|
+
remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
|
|
148
|
+
rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
|
|
149
|
+
shikiConfig: options.shikiConfig ?? defaults.shikiConfig
|
|
150
|
+
};
|
|
151
|
+
}
|
|
132
152
|
function escapeViteEnvReferences(code) {
|
|
133
153
|
return code.replace(/import\.meta\.env/g, "import\\u002Emeta.env");
|
|
134
154
|
}
|
package/dist/plugins.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { MdxOptions } from './index.js';
|
|
|
5
5
|
export declare function recmaInjectImportMetaEnvPlugin({ importMetaEnv, }: {
|
|
6
6
|
importMetaEnv: Record<string, any>;
|
|
7
7
|
}): (tree: any) => void;
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function rehypeApplyFrontmatterExport(pageFrontmatter: Record<string, any>): (tree: any, vfile: VFile) => void;
|
|
8
|
+
export declare function rehypeApplyFrontmatterExport(): (tree: any, vfile: VFile) => void;
|
|
10
9
|
export declare function getRemarkPlugins(mdxOptions: MdxOptions, config: AstroConfig): Promise<MdxRollupPluginOptions['remarkPlugins']>;
|
|
11
|
-
export declare function getRehypePlugins(mdxOptions: MdxOptions
|
|
10
|
+
export declare function getRehypePlugins(mdxOptions: MdxOptions): MdxRollupPluginOptions['rehypePlugins'];
|
package/dist/plugins.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { rehypeHeadingIds } from "@astrojs/markdown-remark";
|
|
2
|
+
import {
|
|
3
|
+
InvalidAstroDataError,
|
|
4
|
+
safelyGetAstroData
|
|
5
|
+
} from "@astrojs/markdown-remark/dist/internal.js";
|
|
2
6
|
import { nodeTypes } from "@mdx-js/mdx";
|
|
3
7
|
import { visit as estreeVisit } from "estree-util-visit";
|
|
4
8
|
import { bold, yellow } from "kleur/colors";
|
|
5
9
|
import { pathToFileURL } from "node:url";
|
|
6
10
|
import rehypeRaw from "rehype-raw";
|
|
7
11
|
import remarkGfm from "remark-gfm";
|
|
8
|
-
import remarkSmartypants from "remark-smartypants";
|
|
9
12
|
import { visit } from "unist-util-visit";
|
|
10
13
|
import { rehypeInjectHeadingsExport } from "./rehype-collect-headings.js";
|
|
11
14
|
import rehypeMetaString from "./rehype-meta-string.js";
|
|
@@ -34,26 +37,16 @@ function recmaInjectImportMetaEnvPlugin({
|
|
|
34
37
|
});
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
|
-
function
|
|
40
|
+
function rehypeApplyFrontmatterExport() {
|
|
38
41
|
return function(tree, vfile) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return function(tree, vfile) {
|
|
46
|
-
const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data);
|
|
47
|
-
const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter };
|
|
42
|
+
const astroData = safelyGetAstroData(vfile.data);
|
|
43
|
+
if (astroData instanceof InvalidAstroDataError)
|
|
44
|
+
throw new Error(
|
|
45
|
+
'[MDX] A remark or rehype plugin attempted to inject invalid frontmatter. Ensure "astro.frontmatter" is set to a valid JSON object that is not `null` or `undefined`.'
|
|
46
|
+
);
|
|
47
|
+
const { frontmatter } = astroData;
|
|
48
48
|
const exportNodes = [
|
|
49
|
-
jsToTreeNode(
|
|
50
|
-
`export const frontmatter = ${JSON.stringify(
|
|
51
|
-
frontmatter
|
|
52
|
-
)};
|
|
53
|
-
export const _internal = { injectedFrontmatter: ${JSON.stringify(
|
|
54
|
-
injectedFrontmatter
|
|
55
|
-
)} };`
|
|
56
|
-
)
|
|
49
|
+
jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`)
|
|
57
50
|
];
|
|
58
51
|
if (frontmatter.layout) {
|
|
59
52
|
exportNodes.unshift(
|
|
@@ -118,68 +111,37 @@ function toRemarkContentRelImageError({ srcDir }) {
|
|
|
118
111
|
};
|
|
119
112
|
};
|
|
120
113
|
}
|
|
121
|
-
const DEFAULT_REMARK_PLUGINS = [remarkGfm, remarkSmartypants];
|
|
122
|
-
const DEFAULT_REHYPE_PLUGINS = [];
|
|
123
114
|
async function getRemarkPlugins(mdxOptions, config) {
|
|
124
|
-
let remarkPlugins = [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
switch (mdxOptions.extendPlugins) {
|
|
128
|
-
case false:
|
|
129
|
-
break;
|
|
130
|
-
case "astroDefaults":
|
|
131
|
-
remarkPlugins = [...remarkPlugins, ...DEFAULT_REMARK_PLUGINS];
|
|
132
|
-
break;
|
|
133
|
-
default:
|
|
134
|
-
remarkPlugins = [
|
|
135
|
-
...remarkPlugins,
|
|
136
|
-
...markdownShouldExtendDefaultPlugins(config) ? DEFAULT_REMARK_PLUGINS : [],
|
|
137
|
-
...ignoreStringPlugins(config.markdown.remarkPlugins ?? [])
|
|
138
|
-
];
|
|
139
|
-
break;
|
|
115
|
+
let remarkPlugins = [];
|
|
116
|
+
if (mdxOptions.syntaxHighlight === "shiki") {
|
|
117
|
+
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
|
|
140
118
|
}
|
|
141
|
-
if (
|
|
142
|
-
remarkPlugins.push([await remarkShiki(config.markdown.shikiConfig)]);
|
|
143
|
-
}
|
|
144
|
-
if (config.markdown.syntaxHighlight === "prism") {
|
|
119
|
+
if (mdxOptions.syntaxHighlight === "prism") {
|
|
145
120
|
remarkPlugins.push(remarkPrism);
|
|
146
121
|
}
|
|
147
|
-
|
|
122
|
+
if (mdxOptions.gfm) {
|
|
123
|
+
remarkPlugins.push(remarkGfm);
|
|
124
|
+
}
|
|
125
|
+
remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)];
|
|
148
126
|
if (config.experimental.contentCollections) {
|
|
149
127
|
remarkPlugins.push(toRemarkContentRelImageError(config));
|
|
150
128
|
}
|
|
151
129
|
return remarkPlugins;
|
|
152
130
|
}
|
|
153
|
-
function getRehypePlugins(mdxOptions
|
|
131
|
+
function getRehypePlugins(mdxOptions) {
|
|
154
132
|
let rehypePlugins = [
|
|
155
133
|
rehypeMetaString,
|
|
156
134
|
[rehypeRaw, { passThrough: nodeTypes }]
|
|
157
135
|
];
|
|
158
|
-
switch (mdxOptions.extendPlugins) {
|
|
159
|
-
case false:
|
|
160
|
-
break;
|
|
161
|
-
case "astroDefaults":
|
|
162
|
-
rehypePlugins = [...rehypePlugins, ...DEFAULT_REHYPE_PLUGINS];
|
|
163
|
-
break;
|
|
164
|
-
default:
|
|
165
|
-
rehypePlugins = [
|
|
166
|
-
...rehypePlugins,
|
|
167
|
-
...markdownShouldExtendDefaultPlugins(config) ? DEFAULT_REHYPE_PLUGINS : [],
|
|
168
|
-
...ignoreStringPlugins(config.markdown.rehypePlugins ?? [])
|
|
169
|
-
];
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
136
|
rehypePlugins = [
|
|
173
137
|
...rehypePlugins,
|
|
174
|
-
...mdxOptions.rehypePlugins
|
|
138
|
+
...ignoreStringPlugins(mdxOptions.rehypePlugins),
|
|
175
139
|
rehypeHeadingIds,
|
|
176
|
-
rehypeInjectHeadingsExport
|
|
140
|
+
rehypeInjectHeadingsExport,
|
|
141
|
+
rehypeApplyFrontmatterExport
|
|
177
142
|
];
|
|
178
143
|
return rehypePlugins;
|
|
179
144
|
}
|
|
180
|
-
function markdownShouldExtendDefaultPlugins(config) {
|
|
181
|
-
return config.markdown.extendDefaultPlugins || config.markdown.remarkPlugins.length === 0 && config.markdown.rehypePlugins.length === 0;
|
|
182
|
-
}
|
|
183
145
|
function ignoreStringPlugins(plugins) {
|
|
184
146
|
let validPlugins = [];
|
|
185
147
|
let hasInvalidPlugin = false;
|
|
@@ -201,29 +163,6 @@ function ignoreStringPlugins(plugins) {
|
|
|
201
163
|
}
|
|
202
164
|
return validPlugins;
|
|
203
165
|
}
|
|
204
|
-
function isValidAstroData(obj) {
|
|
205
|
-
if (typeof obj === "object" && obj !== null && obj.hasOwnProperty("frontmatter")) {
|
|
206
|
-
const { frontmatter } = obj;
|
|
207
|
-
try {
|
|
208
|
-
JSON.stringify(frontmatter);
|
|
209
|
-
} catch {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
return typeof frontmatter === "object" && frontmatter !== null;
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
function safelyGetAstroData(vfileData) {
|
|
217
|
-
const { astro } = vfileData;
|
|
218
|
-
if (!astro)
|
|
219
|
-
return { frontmatter: {} };
|
|
220
|
-
if (!isValidAstroData(astro)) {
|
|
221
|
-
throw Error(
|
|
222
|
-
`[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
return astro;
|
|
226
|
-
}
|
|
227
166
|
function getImportMetaEnvVariableName(node) {
|
|
228
167
|
try {
|
|
229
168
|
if (node.object.type !== "MemberExpression" || node.property.type !== "Identifier")
|
|
@@ -248,6 +187,5 @@ export {
|
|
|
248
187
|
getRehypePlugins,
|
|
249
188
|
getRemarkPlugins,
|
|
250
189
|
recmaInjectImportMetaEnvPlugin,
|
|
251
|
-
rehypeApplyFrontmatterExport
|
|
252
|
-
remarkInitializeAstroData
|
|
190
|
+
rehypeApplyFrontmatterExport
|
|
253
191
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/mdx",
|
|
3
3
|
"description": "Use MDX within Astro",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.15.0-beta.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"./package.json": "./package.json"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@astrojs/markdown-remark": "^
|
|
26
|
+
"@astrojs/markdown-remark": "^2.0.0-beta.0",
|
|
27
27
|
"@astrojs/prism": "^1.0.2",
|
|
28
28
|
"@mdx-js/mdx": "^2.1.2",
|
|
29
29
|
"@mdx-js/rollup": "^2.1.1",
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
"rehype-raw": "^6.1.1",
|
|
37
37
|
"remark-frontmatter": "^4.0.1",
|
|
38
38
|
"remark-gfm": "^3.0.1",
|
|
39
|
-
"remark-smartypants": "^2.0.0",
|
|
40
39
|
"shiki": "^0.11.1",
|
|
41
40
|
"unist-util-visit": "^4.1.0",
|
|
42
41
|
"vfile": "^5.3.2"
|
|
@@ -48,7 +47,7 @@
|
|
|
48
47
|
"@types/mdast": "^3.0.10",
|
|
49
48
|
"@types/mocha": "^9.1.1",
|
|
50
49
|
"@types/yargs-parser": "^21.0.0",
|
|
51
|
-
"astro": "
|
|
50
|
+
"astro": "2.0.0-beta.0",
|
|
52
51
|
"astro-scripts": "0.0.9",
|
|
53
52
|
"chai": "^4.3.6",
|
|
54
53
|
"cheerio": "^1.0.0-rc.11",
|
|
@@ -61,7 +60,7 @@
|
|
|
61
60
|
"remark-rehype": "^10.1.0",
|
|
62
61
|
"remark-shiki-twoslash": "^3.1.0",
|
|
63
62
|
"remark-toc": "^8.0.1",
|
|
64
|
-
"vite": "^
|
|
63
|
+
"vite": "^4.0.3"
|
|
65
64
|
},
|
|
66
65
|
"engines": {
|
|
67
66
|
"node": "^14.18.0 || >=16.12.0"
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { markdownConfigDefaults } from '@astrojs/markdown-remark';
|
|
2
|
+
import { toRemarkInitializeAstroData } from '@astrojs/markdown-remark/dist/internal.js';
|
|
1
3
|
import { compile as mdxCompile } from '@mdx-js/mdx';
|
|
2
4
|
import { PluggableList } from '@mdx-js/mdx/lib/core.js';
|
|
3
5
|
import mdxPlugin, { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
|
|
@@ -7,12 +9,7 @@ import fs from 'node:fs/promises';
|
|
|
7
9
|
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
|
|
8
10
|
import { VFile } from 'vfile';
|
|
9
11
|
import type { Plugin as VitePlugin } from 'vite';
|
|
10
|
-
import {
|
|
11
|
-
getRehypePlugins,
|
|
12
|
-
getRemarkPlugins,
|
|
13
|
-
recmaInjectImportMetaEnvPlugin,
|
|
14
|
-
rehypeApplyFrontmatterExport,
|
|
15
|
-
} from './plugins.js';
|
|
12
|
+
import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from './plugins.js';
|
|
16
13
|
import { getFileInfo, parseFrontmatter } from './utils.js';
|
|
17
14
|
|
|
18
15
|
const RAW_CONTENT_ERROR =
|
|
@@ -21,44 +18,41 @@ const RAW_CONTENT_ERROR =
|
|
|
21
18
|
const COMPILED_CONTENT_ERROR =
|
|
22
19
|
'MDX does not support compiledContent()! If you need to read the HTML contents to calculate values (ex. reading time), we suggest injecting frontmatter via rehype plugins. Learn more on our docs: https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins';
|
|
23
20
|
|
|
24
|
-
export type MdxOptions = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* - "astroDefaults" - inherit Astro’s default plugins only ([see defaults](https://docs.astro.build/en/reference/configuration-reference/#markdownextenddefaultplugins))
|
|
33
|
-
* - false - do not inherit any plugins
|
|
34
|
-
*/
|
|
35
|
-
extendPlugins?: 'markdown' | 'astroDefaults' | false;
|
|
36
|
-
remarkRehype?: RemarkRehypeOptions;
|
|
21
|
+
export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
|
|
22
|
+
extendMarkdownConfig: boolean;
|
|
23
|
+
recmaPlugins: PluggableList;
|
|
24
|
+
// Markdown allows strings as remark and rehype plugins.
|
|
25
|
+
// This is not supported by the MDX compiler, so override types here.
|
|
26
|
+
remarkPlugins: PluggableList;
|
|
27
|
+
rehypePlugins: PluggableList;
|
|
28
|
+
remarkRehype: RemarkRehypeOptions;
|
|
37
29
|
};
|
|
38
30
|
|
|
39
|
-
export default function mdx(
|
|
31
|
+
export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration {
|
|
40
32
|
return {
|
|
41
33
|
name: '@astrojs/mdx',
|
|
42
34
|
hooks: {
|
|
43
35
|
'astro:config:setup': async ({ updateConfig, config, addPageExtension, command }: any) => {
|
|
44
36
|
addPageExtension('.mdx');
|
|
45
|
-
mdxOptions.extendPlugins ??= 'markdown';
|
|
46
37
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
const extendMarkdownConfig =
|
|
39
|
+
partialMdxOptions.extendMarkdownConfig ?? defaultOptions.extendMarkdownConfig;
|
|
40
|
+
|
|
41
|
+
const mdxOptions = applyDefaultOptions({
|
|
42
|
+
options: partialMdxOptions,
|
|
43
|
+
defaults: extendMarkdownConfig ? config.markdown : defaultOptions,
|
|
44
|
+
});
|
|
51
45
|
|
|
52
46
|
const mdxPluginOpts: MdxRollupPluginOptions = {
|
|
53
47
|
remarkPlugins: await getRemarkPlugins(mdxOptions, config),
|
|
54
|
-
rehypePlugins: getRehypePlugins(mdxOptions
|
|
48
|
+
rehypePlugins: getRehypePlugins(mdxOptions),
|
|
55
49
|
recmaPlugins: mdxOptions.recmaPlugins,
|
|
50
|
+
remarkRehypeOptions: mdxOptions.remarkRehype,
|
|
56
51
|
jsx: true,
|
|
57
52
|
jsxImportSource: 'astro',
|
|
58
53
|
// Note: disable `.md` (and other alternative extensions for markdown files like `.markdown`) support
|
|
59
54
|
format: 'mdx',
|
|
60
55
|
mdExtensions: [],
|
|
61
|
-
remarkRehypeOptions,
|
|
62
56
|
};
|
|
63
57
|
|
|
64
58
|
let importMetaEnv: Record<string, any> = {
|
|
@@ -86,9 +80,10 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
|
|
|
86
80
|
const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
|
|
87
81
|
const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
|
|
88
82
|
...mdxPluginOpts,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
(
|
|
83
|
+
remarkPlugins: [
|
|
84
|
+
// Ensure `data.astro` is available to all remark plugins
|
|
85
|
+
toRemarkInitializeAstroData({ userFrontmatter: frontmatter }),
|
|
86
|
+
...(mdxPluginOpts.remarkPlugins ?? []),
|
|
92
87
|
],
|
|
93
88
|
recmaPlugins: [
|
|
94
89
|
...(mdxPluginOpts.recmaPlugins ?? []),
|
|
@@ -169,6 +164,34 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
|
|
|
169
164
|
};
|
|
170
165
|
}
|
|
171
166
|
|
|
167
|
+
const defaultOptions: MdxOptions = {
|
|
168
|
+
...markdownConfigDefaults,
|
|
169
|
+
extendMarkdownConfig: true,
|
|
170
|
+
recmaPlugins: [],
|
|
171
|
+
remarkPlugins: [],
|
|
172
|
+
rehypePlugins: [],
|
|
173
|
+
remarkRehype: {},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
function applyDefaultOptions({
|
|
177
|
+
options,
|
|
178
|
+
defaults,
|
|
179
|
+
}: {
|
|
180
|
+
options: Partial<MdxOptions>;
|
|
181
|
+
defaults: MdxOptions;
|
|
182
|
+
}): MdxOptions {
|
|
183
|
+
return {
|
|
184
|
+
syntaxHighlight: options.syntaxHighlight ?? defaults.syntaxHighlight,
|
|
185
|
+
extendMarkdownConfig: options.extendMarkdownConfig ?? defaults.extendMarkdownConfig,
|
|
186
|
+
recmaPlugins: options.recmaPlugins ?? defaults.recmaPlugins,
|
|
187
|
+
remarkRehype: options.remarkRehype ?? defaults.remarkRehype,
|
|
188
|
+
gfm: options.gfm ?? defaults.gfm,
|
|
189
|
+
remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
|
|
190
|
+
rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
|
|
191
|
+
shikiConfig: options.shikiConfig ?? defaults.shikiConfig,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
172
195
|
// Converts the first dot in `import.meta.env` to its Unicode escape sequence,
|
|
173
196
|
// which prevents Vite from replacing strings like `import.meta.env.SITE`
|
|
174
197
|
// in our JS representation of loaded Markdown files
|
package/src/plugins.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
|
2
|
+
import {
|
|
3
|
+
InvalidAstroDataError,
|
|
4
|
+
safelyGetAstroData,
|
|
5
|
+
} from '@astrojs/markdown-remark/dist/internal.js';
|
|
2
6
|
import { nodeTypes } from '@mdx-js/mdx';
|
|
3
7
|
import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
|
|
4
8
|
import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
|
|
5
|
-
import type { AstroConfig
|
|
9
|
+
import type { AstroConfig } from 'astro';
|
|
6
10
|
import type { Literal, MemberExpression } from 'estree';
|
|
7
11
|
import { visit as estreeVisit } from 'estree-util-visit';
|
|
8
12
|
import { bold, yellow } from 'kleur/colors';
|
|
@@ -10,9 +14,8 @@ import type { Image } from 'mdast';
|
|
|
10
14
|
import { pathToFileURL } from 'node:url';
|
|
11
15
|
import rehypeRaw from 'rehype-raw';
|
|
12
16
|
import remarkGfm from 'remark-gfm';
|
|
13
|
-
import remarkSmartypants from 'remark-smartypants';
|
|
14
17
|
import { visit } from 'unist-util-visit';
|
|
15
|
-
import type {
|
|
18
|
+
import type { VFile } from 'vfile';
|
|
16
19
|
import { MdxOptions } from './index.js';
|
|
17
20
|
import { rehypeInjectHeadingsExport } from './rehype-collect-headings.js';
|
|
18
21
|
import rehypeMetaString from './rehype-meta-string.js';
|
|
@@ -47,26 +50,18 @@ export function recmaInjectImportMetaEnvPlugin({
|
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
export function
|
|
53
|
+
export function rehypeApplyFrontmatterExport() {
|
|
51
54
|
return function (tree: any, vfile: VFile) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data);
|
|
61
|
-
const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter };
|
|
55
|
+
const astroData = safelyGetAstroData(vfile.data);
|
|
56
|
+
if (astroData instanceof InvalidAstroDataError)
|
|
57
|
+
throw new Error(
|
|
58
|
+
// Copied from Astro core `errors-data`
|
|
59
|
+
// TODO: find way to import error data from core
|
|
60
|
+
'[MDX] A remark or rehype plugin attempted to inject invalid frontmatter. Ensure "astro.frontmatter" is set to a valid JSON object that is not `null` or `undefined`.'
|
|
61
|
+
);
|
|
62
|
+
const { frontmatter } = astroData;
|
|
62
63
|
const exportNodes = [
|
|
63
|
-
jsToTreeNode(
|
|
64
|
-
`export const frontmatter = ${JSON.stringify(
|
|
65
|
-
frontmatter
|
|
66
|
-
)};\nexport const _internal = { injectedFrontmatter: ${JSON.stringify(
|
|
67
|
-
injectedFrontmatter
|
|
68
|
-
)} };`
|
|
69
|
-
),
|
|
64
|
+
jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`),
|
|
70
65
|
];
|
|
71
66
|
if (frontmatter.layout) {
|
|
72
67
|
// NOTE(bholmesdev) 08-22-2022
|
|
@@ -144,39 +139,22 @@ function toRemarkContentRelImageError({ srcDir }: { srcDir: URL }) {
|
|
|
144
139
|
};
|
|
145
140
|
}
|
|
146
141
|
|
|
147
|
-
const DEFAULT_REMARK_PLUGINS: PluggableList = [remarkGfm, remarkSmartypants];
|
|
148
|
-
const DEFAULT_REHYPE_PLUGINS: PluggableList = [];
|
|
149
|
-
|
|
150
142
|
export async function getRemarkPlugins(
|
|
151
143
|
mdxOptions: MdxOptions,
|
|
152
144
|
config: AstroConfig
|
|
153
145
|
): Promise<MdxRollupPluginOptions['remarkPlugins']> {
|
|
154
|
-
let remarkPlugins: PluggableList = [
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
];
|
|
158
|
-
switch (mdxOptions.extendPlugins) {
|
|
159
|
-
case false:
|
|
160
|
-
break;
|
|
161
|
-
case 'astroDefaults':
|
|
162
|
-
remarkPlugins = [...remarkPlugins, ...DEFAULT_REMARK_PLUGINS];
|
|
163
|
-
break;
|
|
164
|
-
default:
|
|
165
|
-
remarkPlugins = [
|
|
166
|
-
...remarkPlugins,
|
|
167
|
-
...(markdownShouldExtendDefaultPlugins(config) ? DEFAULT_REMARK_PLUGINS : []),
|
|
168
|
-
...ignoreStringPlugins(config.markdown.remarkPlugins ?? []),
|
|
169
|
-
];
|
|
170
|
-
break;
|
|
146
|
+
let remarkPlugins: PluggableList = [];
|
|
147
|
+
if (mdxOptions.syntaxHighlight === 'shiki') {
|
|
148
|
+
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
|
|
171
149
|
}
|
|
172
|
-
if (
|
|
173
|
-
remarkPlugins.push([await remarkShiki(config.markdown.shikiConfig)]);
|
|
174
|
-
}
|
|
175
|
-
if (config.markdown.syntaxHighlight === 'prism') {
|
|
150
|
+
if (mdxOptions.syntaxHighlight === 'prism') {
|
|
176
151
|
remarkPlugins.push(remarkPrism);
|
|
177
152
|
}
|
|
153
|
+
if (mdxOptions.gfm) {
|
|
154
|
+
remarkPlugins.push(remarkGfm);
|
|
155
|
+
}
|
|
178
156
|
|
|
179
|
-
remarkPlugins = [...remarkPlugins, ...(mdxOptions.remarkPlugins
|
|
157
|
+
remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)];
|
|
180
158
|
|
|
181
159
|
// Apply last in case user plugins resolve relative image paths
|
|
182
160
|
if (config.experimental.contentCollections) {
|
|
@@ -185,49 +163,27 @@ export async function getRemarkPlugins(
|
|
|
185
163
|
return remarkPlugins;
|
|
186
164
|
}
|
|
187
165
|
|
|
188
|
-
export function getRehypePlugins(
|
|
189
|
-
mdxOptions: MdxOptions,
|
|
190
|
-
config: AstroConfig
|
|
191
|
-
): MdxRollupPluginOptions['rehypePlugins'] {
|
|
166
|
+
export function getRehypePlugins(mdxOptions: MdxOptions): MdxRollupPluginOptions['rehypePlugins'] {
|
|
192
167
|
let rehypePlugins: PluggableList = [
|
|
193
168
|
// ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters
|
|
194
169
|
rehypeMetaString,
|
|
195
170
|
// rehypeRaw allows custom syntax highlighters to work without added config
|
|
196
171
|
[rehypeRaw, { passThrough: nodeTypes }] as any,
|
|
197
172
|
];
|
|
198
|
-
switch (mdxOptions.extendPlugins) {
|
|
199
|
-
case false:
|
|
200
|
-
break;
|
|
201
|
-
case 'astroDefaults':
|
|
202
|
-
rehypePlugins = [...rehypePlugins, ...DEFAULT_REHYPE_PLUGINS];
|
|
203
|
-
break;
|
|
204
|
-
default:
|
|
205
|
-
rehypePlugins = [
|
|
206
|
-
...rehypePlugins,
|
|
207
|
-
...(markdownShouldExtendDefaultPlugins(config) ? DEFAULT_REHYPE_PLUGINS : []),
|
|
208
|
-
...ignoreStringPlugins(config.markdown.rehypePlugins ?? []),
|
|
209
|
-
];
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
173
|
|
|
213
174
|
rehypePlugins = [
|
|
214
175
|
...rehypePlugins,
|
|
215
|
-
...(mdxOptions.rehypePlugins
|
|
176
|
+
...ignoreStringPlugins(mdxOptions.rehypePlugins),
|
|
216
177
|
// getHeadings() is guaranteed by TS, so this must be included.
|
|
217
178
|
// We run `rehypeHeadingIds` _last_ to respect any custom IDs set by user plugins.
|
|
218
179
|
rehypeHeadingIds,
|
|
219
180
|
rehypeInjectHeadingsExport,
|
|
181
|
+
// computed from `astro.data.frontmatter` in VFile data
|
|
182
|
+
rehypeApplyFrontmatterExport,
|
|
220
183
|
];
|
|
221
184
|
return rehypePlugins;
|
|
222
185
|
}
|
|
223
186
|
|
|
224
|
-
function markdownShouldExtendDefaultPlugins(config: AstroConfig): boolean {
|
|
225
|
-
return (
|
|
226
|
-
config.markdown.extendDefaultPlugins ||
|
|
227
|
-
(config.markdown.remarkPlugins.length === 0 && config.markdown.rehypePlugins.length === 0)
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
187
|
function ignoreStringPlugins(plugins: any[]) {
|
|
232
188
|
let validPlugins: PluggableList = [];
|
|
233
189
|
let hasInvalidPlugin = false;
|
|
@@ -250,41 +206,6 @@ function ignoreStringPlugins(plugins: any[]) {
|
|
|
250
206
|
return validPlugins;
|
|
251
207
|
}
|
|
252
208
|
|
|
253
|
-
/**
|
|
254
|
-
* Copied from markdown utils
|
|
255
|
-
* @see "vite-plugin-utils"
|
|
256
|
-
*/
|
|
257
|
-
function isValidAstroData(obj: unknown): obj is MarkdownAstroData {
|
|
258
|
-
if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) {
|
|
259
|
-
const { frontmatter } = obj as any;
|
|
260
|
-
try {
|
|
261
|
-
// ensure frontmatter is JSON-serializable
|
|
262
|
-
JSON.stringify(frontmatter);
|
|
263
|
-
} catch {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
return typeof frontmatter === 'object' && frontmatter !== null;
|
|
267
|
-
}
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Copied from markdown utils
|
|
273
|
-
* @see "vite-plugin-utils"
|
|
274
|
-
*/
|
|
275
|
-
function safelyGetAstroData(vfileData: Data): MarkdownAstroData {
|
|
276
|
-
const { astro } = vfileData;
|
|
277
|
-
|
|
278
|
-
if (!astro) return { frontmatter: {} };
|
|
279
|
-
if (!isValidAstroData(astro)) {
|
|
280
|
-
throw Error(
|
|
281
|
-
`[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!`
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return astro;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
209
|
/**
|
|
289
210
|
* Check if estree entry is "import.meta.env.VARIABLE"
|
|
290
211
|
* If it is, return the variable name (i.e. "VARIABLE")
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { defineConfig } from 'astro/config';
|
|
2
2
|
import mdx from '@astrojs/mdx';
|
|
3
|
-
import { rehypeReadingTime, remarkTitle } from './src/markdown-plugins.mjs';
|
|
3
|
+
import { rehypeReadingTime, remarkDescription, remarkTitle } from './src/markdown-plugins.mjs';
|
|
4
4
|
|
|
5
5
|
// https://astro.build/config
|
|
6
6
|
export default defineConfig({
|
|
7
7
|
site: 'https://astro.build/',
|
|
8
8
|
integrations: [mdx({
|
|
9
|
-
remarkPlugins: [remarkTitle],
|
|
9
|
+
remarkPlugins: [remarkTitle, remarkDescription],
|
|
10
10
|
rehypePlugins: [rehypeReadingTime],
|
|
11
11
|
})],
|
|
12
12
|
});
|
|
@@ -18,3 +18,10 @@ export function remarkTitle() {
|
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
export function remarkDescription() {
|
|
23
|
+
return function (tree, vfile) {
|
|
24
|
+
const { frontmatter } = vfile.data.astro;
|
|
25
|
+
frontmatter.description = `Processed by remarkDescription plugin: ${frontmatter.description}`
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -33,14 +33,11 @@ describe('MDX frontmatter injection', () => {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
it('
|
|
36
|
+
it('allow user frontmatter mutation', async () => {
|
|
37
37
|
const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
const titles = frontmatterByPage.map((frontmatter = {}) => frontmatter.title);
|
|
42
|
-
expect(titles).to.contain('Overridden title');
|
|
43
|
-
expect(readingTimes).to.contain('1000 min read');
|
|
38
|
+
const descriptions = frontmatterByPage.map((frontmatter = {}) => frontmatter.description);
|
|
39
|
+
expect(descriptions).to.contain('Processed by remarkDescription plugin: Page 1 description');
|
|
40
|
+
expect(descriptions).to.contain('Processed by remarkDescription plugin: Page 2 description');
|
|
44
41
|
});
|
|
45
42
|
|
|
46
43
|
it('passes injected frontmatter to layouts', async () => {
|
package/test/mdx-plugins.test.js
CHANGED
|
@@ -80,91 +80,57 @@ describe('MDX plugins', () => {
|
|
|
80
80
|
expect(selectTocLink(document)).to.be.null;
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
rehypePlugins: [rehypeExamplePlugin],
|
|
133
|
-
extendPlugins: 'astroDefaults',
|
|
134
|
-
}),
|
|
135
|
-
],
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const html = await fixture.readFile(FILE);
|
|
139
|
-
const { document } = parseHTML(html);
|
|
140
|
-
|
|
141
|
-
expect(selectGfmLink(document)).to.not.be.null;
|
|
142
|
-
// remark and rehype plugins still respected
|
|
143
|
-
expect(selectRemarkExample(document)).to.not.be.null;
|
|
144
|
-
expect(selectRehypeExample(document)).to.not.be.null;
|
|
145
|
-
// Does NOT inherit TOC from markdown config
|
|
146
|
-
expect(selectTocLink(document)).to.be.null;
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('does not extend default plugins with extendPlugins: false', async () => {
|
|
150
|
-
const fixture = await buildFixture({
|
|
151
|
-
markdown: {
|
|
152
|
-
remarkPlugins: [remarkExamplePlugin],
|
|
153
|
-
},
|
|
154
|
-
integrations: [
|
|
155
|
-
mdx({
|
|
156
|
-
remarkPlugins: [],
|
|
157
|
-
extendPlugins: false,
|
|
158
|
-
}),
|
|
159
|
-
],
|
|
83
|
+
for (const extendMarkdownConfig of [true, false]) {
|
|
84
|
+
describe(`extendMarkdownConfig = ${extendMarkdownConfig}`, () => {
|
|
85
|
+
let fixture;
|
|
86
|
+
before(async () => {
|
|
87
|
+
fixture = await buildFixture({
|
|
88
|
+
markdown: {
|
|
89
|
+
remarkPlugins: [remarkToc],
|
|
90
|
+
gfm: false,
|
|
91
|
+
},
|
|
92
|
+
integrations: [
|
|
93
|
+
mdx({
|
|
94
|
+
extendMarkdownConfig,
|
|
95
|
+
remarkPlugins: [remarkExamplePlugin],
|
|
96
|
+
rehypePlugins: [rehypeExamplePlugin],
|
|
97
|
+
}),
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('Handles MDX plugins', async () => {
|
|
103
|
+
const html = await fixture.readFile(FILE);
|
|
104
|
+
const { document } = parseHTML(html);
|
|
105
|
+
|
|
106
|
+
expect(selectRemarkExample(document, 'MDX remark plugins not applied.')).to.not.be.null;
|
|
107
|
+
expect(selectRehypeExample(document, 'MDX rehype plugins not applied.')).to.not.be.null;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('Handles Markdown plugins', async () => {
|
|
111
|
+
const html = await fixture.readFile(FILE);
|
|
112
|
+
const { document } = parseHTML(html);
|
|
113
|
+
|
|
114
|
+
expect(
|
|
115
|
+
selectTocLink(
|
|
116
|
+
document,
|
|
117
|
+
'`remarkToc` plugin applied unexpectedly. Should override Markdown config.'
|
|
118
|
+
)
|
|
119
|
+
).to.be.null;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('Handles gfm', async () => {
|
|
123
|
+
const html = await fixture.readFile(FILE);
|
|
124
|
+
const { document } = parseHTML(html);
|
|
125
|
+
|
|
126
|
+
if (extendMarkdownConfig === true) {
|
|
127
|
+
expect(selectGfmLink(document), 'Does not respect `markdown.gfm` option.').to.be.null;
|
|
128
|
+
} else {
|
|
129
|
+
expect(selectGfmLink(document), 'Respects `markdown.gfm` unexpectedly.').to.not.be.null;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
160
132
|
});
|
|
161
|
-
|
|
162
|
-
const html = await fixture.readFile(FILE);
|
|
163
|
-
const { document } = parseHTML(html);
|
|
164
|
-
|
|
165
|
-
expect(selectGfmLink(document)).to.be.null;
|
|
166
|
-
expect(selectRemarkExample(document)).to.be.null;
|
|
167
|
-
});
|
|
133
|
+
}
|
|
168
134
|
|
|
169
135
|
it('supports custom recma plugins', async () => {
|
|
170
136
|
const fixture = await buildFixture({
|
|
@@ -67,6 +67,32 @@ describe('MDX syntax highlighting', () => {
|
|
|
67
67
|
const prismCodeBlock = document.querySelector('pre.language-astro');
|
|
68
68
|
expect(prismCodeBlock).to.not.be.null;
|
|
69
69
|
});
|
|
70
|
+
|
|
71
|
+
for (const extendMarkdownConfig of [true, false]) {
|
|
72
|
+
it(`respects syntaxHighlight when extendMarkdownConfig = ${extendMarkdownConfig}`, async () => {
|
|
73
|
+
const fixture = await loadFixture({
|
|
74
|
+
root: FIXTURE_ROOT,
|
|
75
|
+
markdown: {
|
|
76
|
+
syntaxHighlight: 'shiki',
|
|
77
|
+
},
|
|
78
|
+
integrations: [
|
|
79
|
+
mdx({
|
|
80
|
+
extendMarkdownConfig,
|
|
81
|
+
syntaxHighlight: 'prism',
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
await fixture.build();
|
|
86
|
+
|
|
87
|
+
const html = await fixture.readFile('/index.html');
|
|
88
|
+
const { document } = parseHTML(html);
|
|
89
|
+
|
|
90
|
+
const shikiCodeBlock = document.querySelector('pre.astro-code');
|
|
91
|
+
expect(shikiCodeBlock, 'Markdown config syntaxHighlight used unexpectedly').to.be.null;
|
|
92
|
+
const prismCodeBlock = document.querySelector('pre.language-astro');
|
|
93
|
+
expect(prismCodeBlock).to.not.be.null;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
70
96
|
});
|
|
71
97
|
|
|
72
98
|
it('supports custom highlighter - shiki-twoslash', async () => {
|