@astrojs/mdx 1.0.0-beta.2 → 1.0.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 (106) hide show
  1. package/README.md +83 -19
  2. package/dist/index.d.ts +4 -2
  3. package/dist/index.js +83 -35
  4. package/dist/plugins.d.ts +4 -5
  5. package/dist/plugins.js +36 -80
  6. package/dist/rehype-collect-headings.d.ts +1 -1
  7. package/dist/rehype-meta-string.js +2 -3
  8. package/dist/rehype-optimize-static.d.ts +11 -0
  9. package/dist/rehype-optimize-static.js +62 -0
  10. package/dist/remark-images-to-component.d.ts +2 -0
  11. package/dist/remark-images-to-component.js +83 -0
  12. package/dist/utils.d.ts +2 -1
  13. package/dist/utils.js +23 -15
  14. package/package.json +39 -30
  15. package/template/content-module-types.d.ts +9 -0
  16. package/.turbo/turbo-build.log +0 -5
  17. package/CHANGELOG.md +0 -469
  18. package/src/index.ts +0 -201
  19. package/src/plugins.ts +0 -246
  20. package/src/rehype-collect-headings.ts +0 -11
  21. package/src/rehype-meta-string.ts +0 -17
  22. package/src/remark-prism.ts +0 -18
  23. package/src/remark-shiki.ts +0 -95
  24. package/src/utils.ts +0 -102
  25. package/test/fixtures/mdx-astro-markdown-remarkRehype/src/pages/index.mdx +0 -5
  26. package/test/fixtures/mdx-component/src/components/Test.mdx +0 -3
  27. package/test/fixtures/mdx-component/src/components/WithFragment.mdx +0 -3
  28. package/test/fixtures/mdx-component/src/pages/glob.astro +0 -20
  29. package/test/fixtures/mdx-component/src/pages/index.astro +0 -5
  30. package/test/fixtures/mdx-component/src/pages/w-fragment.astro +0 -5
  31. package/test/fixtures/mdx-escape/src/components/Em.astro +0 -7
  32. package/test/fixtures/mdx-escape/src/components/P.astro +0 -1
  33. package/test/fixtures/mdx-escape/src/components/Title.astro +0 -1
  34. package/test/fixtures/mdx-escape/src/pages/html-tag.mdx +0 -5
  35. package/test/fixtures/mdx-escape/src/pages/index.mdx +0 -13
  36. package/test/fixtures/mdx-frontmatter/src/layouts/Base.astro +0 -38
  37. package/test/fixtures/mdx-frontmatter/src/pages/glob.json.js +0 -9
  38. package/test/fixtures/mdx-frontmatter/src/pages/index.mdx +0 -10
  39. package/test/fixtures/mdx-frontmatter/src/pages/with-headings.mdx +0 -7
  40. package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +0 -12
  41. package/test/fixtures/mdx-frontmatter-injection/node_modules/.bin/astro +0 -17
  42. package/test/fixtures/mdx-frontmatter-injection/package.json +0 -12
  43. package/test/fixtures/mdx-frontmatter-injection/src/layouts/Base.astro +0 -17
  44. package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +0 -27
  45. package/test/fixtures/mdx-frontmatter-injection/src/pages/glob.json.js +0 -6
  46. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +0 -8
  47. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +0 -24
  48. package/test/fixtures/mdx-get-headings/src/pages/pages.json.js +0 -11
  49. package/test/fixtures/mdx-get-headings/src/pages/test-with-jsx-expressions.mdx +0 -8
  50. package/test/fixtures/mdx-get-headings/src/pages/test.mdx +0 -9
  51. package/test/fixtures/mdx-get-static-paths/src/content/1.mdx +0 -5
  52. package/test/fixtures/mdx-get-static-paths/src/pages/[slug].astro +0 -34
  53. package/test/fixtures/mdx-infinite-loop/astro.config.ts +0 -6
  54. package/test/fixtures/mdx-infinite-loop/node_modules/.bin/astro +0 -17
  55. package/test/fixtures/mdx-infinite-loop/package.json +0 -10
  56. package/test/fixtures/mdx-infinite-loop/src/components/Test.js +0 -3
  57. package/test/fixtures/mdx-infinite-loop/src/pages/doc.mdx +0 -6
  58. package/test/fixtures/mdx-infinite-loop/src/pages/index.astro +0 -5
  59. package/test/fixtures/mdx-namespace/astro.config.mjs +0 -6
  60. package/test/fixtures/mdx-namespace/node_modules/.bin/astro +0 -17
  61. package/test/fixtures/mdx-namespace/package.json +0 -10
  62. package/test/fixtures/mdx-namespace/src/components/Component.jsx +0 -6
  63. package/test/fixtures/mdx-namespace/src/pages/object.mdx +0 -3
  64. package/test/fixtures/mdx-namespace/src/pages/star.mdx +0 -3
  65. package/test/fixtures/mdx-page/astro.config.ts +0 -5
  66. package/test/fixtures/mdx-page/node_modules/.bin/astro +0 -17
  67. package/test/fixtures/mdx-page/package.json +0 -9
  68. package/test/fixtures/mdx-page/src/pages/index.mdx +0 -3
  69. package/test/fixtures/mdx-page/src/styles.css +0 -3
  70. package/test/fixtures/mdx-plugins/src/pages/with-plugins.mdx +0 -25
  71. package/test/fixtures/mdx-plus-react/astro.config.mjs +0 -6
  72. package/test/fixtures/mdx-plus-react/node_modules/.bin/astro +0 -17
  73. package/test/fixtures/mdx-plus-react/package.json +0 -10
  74. package/test/fixtures/mdx-plus-react/src/components/Component.jsx +0 -5
  75. package/test/fixtures/mdx-plus-react/src/pages/index.astro +0 -11
  76. package/test/fixtures/mdx-slots/src/components/Slotted.astro +0 -4
  77. package/test/fixtures/mdx-slots/src/components/Test.mdx +0 -15
  78. package/test/fixtures/mdx-slots/src/pages/glob.astro +0 -11
  79. package/test/fixtures/mdx-slots/src/pages/index.astro +0 -5
  80. package/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx +0 -9
  81. package/test/fixtures/mdx-url-export/src/pages/pages.json.js +0 -9
  82. package/test/fixtures/mdx-url-export/src/pages/test-1.mdx +0 -1
  83. package/test/fixtures/mdx-url-export/src/pages/test-2.mdx +0 -1
  84. package/test/fixtures/mdx-url-export/src/pages/with-url-override.mdx +0 -3
  85. package/test/fixtures/mdx-vite-env-vars/astro.config.mjs +0 -9
  86. package/test/fixtures/mdx-vite-env-vars/node_modules/.bin/astro +0 -17
  87. package/test/fixtures/mdx-vite-env-vars/package.json +0 -7
  88. package/test/fixtures/mdx-vite-env-vars/src/pages/frontmatter.json.js +0 -7
  89. package/test/fixtures/mdx-vite-env-vars/src/pages/vite-env-vars.mdx +0 -38
  90. package/test/mdx-astro-markdown-remarkRehype.test.js +0 -85
  91. package/test/mdx-component.test.js +0 -191
  92. package/test/mdx-escape.test.js +0 -32
  93. package/test/mdx-frontmatter-injection.test.js +0 -53
  94. package/test/mdx-frontmatter.test.js +0 -77
  95. package/test/mdx-get-headings.test.js +0 -151
  96. package/test/mdx-get-static-paths.test.js +0 -32
  97. package/test/mdx-infinite-loop.test.js +0 -30
  98. package/test/mdx-namespace.test.js +0 -83
  99. package/test/mdx-page.test.js +0 -64
  100. package/test/mdx-plugins.test.js +0 -250
  101. package/test/mdx-plus-react.test.js +0 -25
  102. package/test/mdx-slots.js +0 -124
  103. package/test/mdx-syntax-highlighting.test.js +0 -145
  104. package/test/mdx-url-export.test.js +0 -28
  105. package/test/mdx-vite-env-vars.test.js +0 -54
  106. package/tsconfig.json +0 -10
package/README.md CHANGED
@@ -42,9 +42,8 @@ npm install @astrojs/mdx
42
42
 
43
43
  Then, apply this integration to your `astro.config.*` file using the `integrations` property:
44
44
 
45
- **`astro.config.mjs`**
46
-
47
- ```js
45
+ ```js ins={3} "mdx()"
46
+ // astro.config.mjs
48
47
  import { defineConfig } from 'astro/config';
49
48
  import mdx from '@astrojs/mdx';
50
49
 
@@ -59,16 +58,18 @@ export default defineConfig({
59
58
  [VS Code](https://code.visualstudio.com/) supports Markdown by default. However, for MDX editor support, you may wish to add the following setting in your VSCode config. This ensures authoring MDX files provides a Markdown-like editor experience.
60
59
 
61
60
  ```json title=".vscode/settings.json"
62
- "files.associations": {
61
+ {
62
+ "files.associations": {
63
63
  "*.mdx": "markdown"
64
+ }
64
65
  }
65
66
  ```
66
67
 
67
68
  ## Usage
68
69
 
69
- With the Astro MDX integration, you can [add MDX pages to your project](https://docs.astro.build/en/guides/markdown-content/#markdown-and-mdx-pages) by adding `.mdx` files within your `src/pages/` directory. You can also [import `.mdx` files](https://docs.astro.build/en/guides/markdown-content/#importing-markdown) into `.astro` files.
70
+ With the Astro MDX integration, you can [add MDX pages to your project](https://docs.astro.build/en/guides/markdown-content/#markdown-and-mdx-pages) by adding `.mdx` files within your `src/pages/` directory. You can also [import `.mdx` files](https://docs.astro.build/en/guides/markdown-content/#importing-markdown) into `.astro` files.
70
71
 
71
- Astro's MDX integration adds extra features to standard MDX, including Markdown-style frontmatter. This allows you to use most of Astro's built-in Markdown features like a [special frontmatter `layout` property](https://docs.astro.build/en/guides/markdown-content/#frontmatter-layout) and a [property for marking a page as a draft](https://docs.astro.build/en/guides/markdown-content/#draft-pages).
72
+ Astro's MDX integration adds extra features to standard MDX, including Markdown-style frontmatter. This allows you to use most of Astro's built-in Markdown features like a [special frontmatter `layout` property](https://docs.astro.build/en/guides/markdown-content/#frontmatter-layout).
72
73
 
73
74
  See how MDX works in Astro with examples in our [Markdown & MDX guide](https://docs.astro.build/en/guides/markdown-content/).
74
75
 
@@ -83,16 +84,17 @@ You can configure how your MDX is rendered with the following options:
83
84
  - [Options inherited from Markdown config](#options-inherited-from-markdown-config)
84
85
  - [`extendMarkdownConfig`](#extendmarkdownconfig)
85
86
  - [`recmaPlugins`](#recmaplugins)
87
+ - [`optimize`](#optimize)
86
88
 
87
89
  ### Options inherited from Markdown config
88
90
 
89
91
  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).
90
92
 
91
93
  :::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.
94
+ 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 overridden for MDX files specifically.
93
95
  :::
94
96
 
95
- ```ts
97
+ ```js
96
98
  // astro.config.mjs
97
99
  import { defineConfig } from 'astro/config';
98
100
  import mdx from '@astrojs/mdx';
@@ -108,9 +110,9 @@ export default defineConfig({
108
110
  rehypePlugins: [rehypeMinifyHtml],
109
111
  remarkRehype: { footnoteLabel: 'Footnotes' },
110
112
  gfm: false,
111
- })
112
- ]
113
- })
113
+ }),
114
+ ],
115
+ });
114
116
  ```
115
117
 
116
118
  :::caution
@@ -128,7 +130,7 @@ MDX will extend [your project's existing Markdown configuration](https://docs.as
128
130
 
129
131
  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:
130
132
 
131
- ```ts
133
+ ```js
132
134
  // astro.config.mjs
133
135
  import { defineConfig } from 'astro/config';
134
136
  import mdx from '@astrojs/mdx';
@@ -148,14 +150,14 @@ export default defineConfig({
148
150
  remarkPlugins: [remarkPlugin2],
149
151
  // `gfm` overridden to `false`
150
152
  gfm: false,
151
- })
152
- ]
153
+ }),
154
+ ],
153
155
  });
154
156
  ```
155
157
 
156
158
  You may also need to disable `markdown` config extension in MDX. For this, set `extendMarkdownConfig` to `false`:
157
159
 
158
- ```ts
160
+ ```js
159
161
  // astro.config.mjs
160
162
  import { defineConfig } from 'astro/config';
161
163
  import mdx from '@astrojs/mdx';
@@ -169,8 +171,8 @@ export default defineConfig({
169
171
  // Markdown config now ignored
170
172
  extendMarkdownConfig: false,
171
173
  // No `remarkPlugins` applied
172
- })
173
- ]
174
+ }),
175
+ ],
174
176
  });
175
177
  ```
176
178
 
@@ -180,9 +182,72 @@ These are plugins that modify the output [estree](https://github.com/estree/estr
180
182
 
181
183
  We suggest [using AST Explorer](https://astexplorer.net/) to play with estree outputs, and trying [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) for searching across JavaScript nodes.
182
184
 
185
+ ### `optimize`
186
+
187
+ - **Type:** `boolean | { customComponentNames?: string[] }`
188
+
189
+ This is an optional configuration setting to optimize the MDX output for faster builds and rendering via an internal rehype plugin. This may be useful if you have many MDX files and notice slow builds. However, this option may generate some unescaped HTML, so make sure your site's interactive parts still work correctly after enabling it.
190
+
191
+ This is disabled by default. To enable MDX optimization, add the following to your MDX integration configuration:
192
+
193
+ ```js
194
+ // astro.config.mjs
195
+ import { defineConfig } from 'astro/config';
196
+ import mdx from '@astrojs/mdx';
197
+
198
+ export default defineConfig({
199
+ integrations: [
200
+ mdx({
201
+ optimize: true,
202
+ }),
203
+ ],
204
+ });
205
+ ```
206
+
207
+ #### `customComponentNames`
208
+
209
+ - **Type:** `string[]`
210
+
211
+ An optional property of `optimize` to prevent the MDX optimizer from handling any [custom components passed to imported MDX content via the components prop](https://docs.astro.build/en/guides/markdown-content/#custom-components-with-imported-mdx).
212
+
213
+ You will need to exclude these components from optimization as the optimizer eagerly converts content into a static string, which will break custom components that needs to be dynamically rendered.
214
+
215
+ For example, the intended MDX output of the following is `<Heading>...</Heading>` in place of every `"<h1>...</h1>"`:
216
+
217
+ ```astro
218
+ ---
219
+ import { Content, components } from '../content.mdx';
220
+ import Heading from '../Heading.astro';
221
+ ---
222
+
223
+ <Content components={{ ...components, h1: Heading }} />
224
+ ```
225
+
226
+ To configure optimization for this using the `customComponentNames` property, specify an array of HTML element names that should be treated as custom components:
227
+
228
+ ```js
229
+ // astro.config.mjs
230
+ import { defineConfig } from 'astro/config';
231
+ import mdx from '@astrojs/mdx';
232
+
233
+ export default defineConfig({
234
+ integrations: [
235
+ mdx({
236
+ optimize: {
237
+ // Prevent the optimizer from handling `h1` elements
238
+ // These will be treated as custom components
239
+ customComponentNames: ['h1'],
240
+ },
241
+ }),
242
+ ],
243
+ });
244
+ ```
245
+
246
+ Note that if your MDX file [configures custom components using `export const components = { ... }`](https://docs.astro.build/en/guides/markdown-content/#assigning-custom-components-to-html-elements), then you do not need to manually configure this option. The optimizer will automatically detect them.
247
+
183
248
  ## Examples
184
249
 
185
- * The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project.
250
+ - The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project.
186
251
 
187
252
  ## Troubleshooting
188
253
 
@@ -199,5 +264,4 @@ This package is maintained by Astro's Core team. You're welcome to submit an iss
199
264
  See [CHANGELOG.md](https://github.com/withastro/astro/tree/main/packages/integrations/mdx/CHANGELOG.md) for a history of changes to this integration.
200
265
 
201
266
  [astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
202
-
203
267
  [astro-ui-frameworks]: https://docs.astro.build/en/core-concepts/framework-components/#using-framework-components
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { markdownConfigDefaults } from '@astrojs/markdown-remark';
2
- import { PluggableList } from '@mdx-js/mdx/lib/core.js';
2
+ import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
3
3
  import type { AstroIntegration } from 'astro';
4
4
  import type { Options as RemarkRehypeOptions } from 'remark-rehype';
5
- export declare type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
5
+ import type { OptimizeOptions } from './rehype-optimize-static.js';
6
+ export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
6
7
  extendMarkdownConfig: boolean;
7
8
  recmaPlugins: PluggableList;
8
9
  remarkPlugins: PluggableList;
9
10
  rehypePlugins: PluggableList;
10
11
  remarkRehype: RemarkRehypeOptions;
12
+ optimize: boolean | OptimizeOptions;
11
13
  };
12
14
  export default function mdx(partialMdxOptions?: Partial<MdxOptions>): AstroIntegration;
package/dist/index.js CHANGED
@@ -1,32 +1,63 @@
1
1
  import { markdownConfigDefaults } from "@astrojs/markdown-remark";
2
2
  import { toRemarkInitializeAstroData } from "@astrojs/markdown-remark/dist/internal.js";
3
3
  import { compile as mdxCompile } from "@mdx-js/mdx";
4
- import mdxPlugin from "@mdx-js/rollup";
4
+ import astroJSXRenderer from "astro/jsx/renderer.js";
5
5
  import { parse as parseESM } from "es-module-lexer";
6
6
  import fs from "node:fs/promises";
7
+ import { fileURLToPath } from "node:url";
8
+ import { SourceMapGenerator } from "source-map";
7
9
  import { VFile } from "vfile";
8
10
  import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from "./plugins.js";
9
- import { getFileInfo, parseFrontmatter } from "./utils.js";
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";
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";
11
+ import { getFileInfo, ignoreStringPlugins, parseFrontmatter } from "./utils.js";
12
12
  function mdx(partialMdxOptions = {}) {
13
13
  return {
14
14
  name: "@astrojs/mdx",
15
15
  hooks: {
16
- "astro:config:setup": async ({ updateConfig, config, addPageExtension, command }) => {
16
+ "astro:config:setup": async (params) => {
17
+ const {
18
+ updateConfig,
19
+ config,
20
+ addPageExtension,
21
+ addContentEntryType,
22
+ command,
23
+ addRenderer
24
+ } = params;
25
+ addRenderer(astroJSXRenderer);
17
26
  addPageExtension(".mdx");
18
- const extendMarkdownConfig = partialMdxOptions.extendMarkdownConfig ?? defaultOptions.extendMarkdownConfig;
27
+ addContentEntryType({
28
+ extensions: [".mdx"],
29
+ async getEntryInfo({ fileUrl, contents }) {
30
+ const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
31
+ return {
32
+ data: parsed.data,
33
+ body: parsed.content,
34
+ slug: parsed.data.slug,
35
+ rawData: parsed.matter
36
+ };
37
+ },
38
+ contentModuleTypes: await fs.readFile(
39
+ new URL("../template/content-module-types.d.ts", import.meta.url),
40
+ "utf-8"
41
+ ),
42
+ // MDX can import scripts and styles,
43
+ // so wrap all MDX files with script / style propagation checks
44
+ handlePropagation: true
45
+ });
46
+ const extendMarkdownConfig = partialMdxOptions.extendMarkdownConfig ?? defaultMdxOptions.extendMarkdownConfig;
19
47
  const mdxOptions = applyDefaultOptions({
20
48
  options: partialMdxOptions,
21
- defaults: extendMarkdownConfig ? config.markdown : defaultOptions
49
+ defaults: markdownConfigToMdxOptions(
50
+ extendMarkdownConfig ? config.markdown : markdownConfigDefaults
51
+ )
22
52
  });
23
53
  const mdxPluginOpts = {
24
- remarkPlugins: await getRemarkPlugins(mdxOptions, config),
54
+ remarkPlugins: await getRemarkPlugins(mdxOptions),
25
55
  rehypePlugins: getRehypePlugins(mdxOptions),
26
56
  recmaPlugins: mdxOptions.recmaPlugins,
27
57
  remarkRehypeOptions: mdxOptions.remarkRehype,
28
58
  jsx: true,
29
59
  jsxImportSource: "astro",
60
+ // Note: disable `.md` (and other alternative extensions for markdown files like `.markdown`) support
30
61
  format: "mdx",
31
62
  mdExtensions: []
32
63
  };
@@ -37,27 +68,43 @@ function mdx(partialMdxOptions = {}) {
37
68
  vite: {
38
69
  plugins: [
39
70
  {
71
+ name: "@mdx-js/rollup",
40
72
  enforce: "pre",
41
- ...mdxPlugin(mdxPluginOpts),
42
73
  configResolved(resolved) {
43
74
  importMetaEnv = { ...importMetaEnv, ...resolved.env };
75
+ const jsxPluginIndex = resolved.plugins.findIndex((p) => p.name === "astro:jsx");
76
+ if (jsxPluginIndex !== -1) {
77
+ const myPluginIndex = resolved.plugins.findIndex(
78
+ (p) => p.name === "@mdx-js/rollup"
79
+ );
80
+ if (myPluginIndex !== -1) {
81
+ const myPlugin = resolved.plugins[myPluginIndex];
82
+ resolved.plugins.splice(myPluginIndex, 1);
83
+ resolved.plugins.splice(jsxPluginIndex, 0, myPlugin);
84
+ }
85
+ }
44
86
  },
87
+ // Override transform to alter code before MDX compilation
88
+ // ex. inject layouts
45
89
  async transform(_, id) {
46
- if (!id.endsWith("mdx"))
90
+ if (!id.endsWith(".mdx"))
47
91
  return;
48
92
  const { fileId } = getFileInfo(id, config);
49
93
  const code = await fs.readFile(fileId, "utf-8");
50
94
  const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
51
95
  const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
52
96
  ...mdxPluginOpts,
97
+ elementAttributeNameCase: "html",
53
98
  remarkPlugins: [
99
+ // Ensure `data.astro` is available to all remark plugins
54
100
  toRemarkInitializeAstroData({ userFrontmatter: frontmatter }),
55
101
  ...mdxPluginOpts.remarkPlugins ?? []
56
102
  ],
57
103
  recmaPlugins: [
58
104
  ...mdxPluginOpts.recmaPlugins ?? [],
59
105
  () => recmaInjectImportMetaEnvPlugin({ importMetaEnv })
60
- ]
106
+ ],
107
+ SourceMapGenerator: config.vite.build?.sourcemap ? SourceMapGenerator : void 0
61
108
  });
62
109
  return {
63
110
  code: escapeViteEnvReferences(String(compiled.value)),
@@ -67,6 +114,7 @@ function mdx(partialMdxOptions = {}) {
67
114
  },
68
115
  {
69
116
  name: "@astrojs/mdx-postprocess",
117
+ // These transforms must happen *after* JSX runtime transformations
70
118
  transform(code, id) {
71
119
  if (!id.endsWith(".mdx"))
72
120
  return;
@@ -79,44 +127,37 @@ function mdx(partialMdxOptions = {}) {
79
127
  code = 'import { Fragment } from "astro/jsx-runtime"\n' + code;
80
128
  }
81
129
  const { fileUrl, fileId } = getFileInfo(id, config);
82
- if (!moduleExports.includes("url")) {
130
+ if (!moduleExports.find(({ n }) => n === "url")) {
83
131
  code += `
84
132
  export const url = ${JSON.stringify(fileUrl)};`;
85
133
  }
86
- if (!moduleExports.includes("file")) {
134
+ if (!moduleExports.find(({ n }) => n === "file")) {
87
135
  code += `
88
136
  export const file = ${JSON.stringify(fileId)};`;
89
137
  }
90
- if (!moduleExports.includes("rawContent")) {
91
- code += `
92
- export function rawContent() { throw new Error(${JSON.stringify(
93
- RAW_CONTENT_ERROR
94
- )}) };`;
95
- }
96
- if (!moduleExports.includes("compiledContent")) {
97
- code += `
98
- export function compiledContent() { throw new Error(${JSON.stringify(
99
- COMPILED_CONTENT_ERROR
100
- )}) };`;
101
- }
102
- if (!moduleExports.includes("Content")) {
138
+ if (!moduleExports.find(({ n }) => n === "Content")) {
139
+ const hasComponents = moduleExports.find(({ n }) => n === "components");
103
140
  code = code.replace("export default MDXContent;", "");
104
141
  code += `
105
142
  export const Content = (props = {}) => MDXContent({
106
143
  ...props,
107
- components: { Fragment, ...props.components },
144
+ components: { Fragment${hasComponents ? ", ...components" : ""}, ...props.components },
108
145
  });
109
146
  export default Content;`;
110
147
  }
111
148
  code += `
149
+ Content[Symbol.for('mdx-component')] = true`;
150
+ code += `
112
151
  Content[Symbol.for('astro.needsHeadRendering')] = !Boolean(frontmatter.layout);`;
152
+ code += `
153
+ Content.moduleId = ${JSON.stringify(id)};`;
113
154
  if (command === "dev") {
114
155
  code += `
115
156
  if (import.meta.hot) {
116
157
  import.meta.hot.decline();
117
158
  }`;
118
159
  }
119
- return escapeViteEnvReferences(code);
160
+ return { code: escapeViteEnvReferences(code), map: null };
120
161
  }
121
162
  }
122
163
  ]
@@ -126,14 +167,20 @@ if (import.meta.hot) {
126
167
  }
127
168
  };
128
169
  }
129
- const defaultOptions = {
130
- ...markdownConfigDefaults,
170
+ const defaultMdxOptions = {
131
171
  extendMarkdownConfig: true,
132
- recmaPlugins: [],
133
- remarkPlugins: [],
134
- rehypePlugins: [],
135
- remarkRehype: {}
172
+ recmaPlugins: []
136
173
  };
174
+ function markdownConfigToMdxOptions(markdownConfig) {
175
+ return {
176
+ ...defaultMdxOptions,
177
+ ...markdownConfig,
178
+ remarkPlugins: ignoreStringPlugins(markdownConfig.remarkPlugins),
179
+ rehypePlugins: ignoreStringPlugins(markdownConfig.rehypePlugins),
180
+ remarkRehype: markdownConfig.remarkRehype ?? {},
181
+ optimize: false
182
+ };
183
+ }
137
184
  function applyDefaultOptions({
138
185
  options,
139
186
  defaults
@@ -147,7 +194,8 @@ function applyDefaultOptions({
147
194
  smartypants: options.smartypants ?? defaults.smartypants,
148
195
  remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
149
196
  rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
150
- shikiConfig: options.shikiConfig ?? defaults.shikiConfig
197
+ shikiConfig: options.shikiConfig ?? defaults.shikiConfig,
198
+ optimize: options.optimize ?? defaults.optimize
151
199
  };
152
200
  }
153
201
  function escapeViteEnvReferences(code) {
package/dist/plugins.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
2
- import type { AstroConfig } from 'astro';
1
+ import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
3
2
  import type { VFile } from 'vfile';
4
- import { MdxOptions } from './index.js';
3
+ import type { MdxOptions } from './index.js';
5
4
  export declare function recmaInjectImportMetaEnvPlugin({ importMetaEnv, }: {
6
5
  importMetaEnv: Record<string, any>;
7
6
  }): (tree: any) => void;
8
7
  export declare function rehypeApplyFrontmatterExport(): (tree: any, vfile: VFile) => void;
9
- export declare function getRemarkPlugins(mdxOptions: MdxOptions, config: AstroConfig): Promise<MdxRollupPluginOptions['remarkPlugins']>;
10
- export declare function getRehypePlugins(mdxOptions: MdxOptions): MdxRollupPluginOptions['rehypePlugins'];
8
+ export declare function getRemarkPlugins(mdxOptions: MdxOptions): Promise<PluggableList>;
9
+ export declare function getRehypePlugins(mdxOptions: MdxOptions): PluggableList;
package/dist/plugins.js CHANGED
@@ -1,21 +1,21 @@
1
- import { rehypeHeadingIds } from "@astrojs/markdown-remark";
1
+ import { rehypeHeadingIds, remarkCollectImages } from "@astrojs/markdown-remark";
2
2
  import {
3
3
  InvalidAstroDataError,
4
4
  safelyGetAstroData
5
5
  } from "@astrojs/markdown-remark/dist/internal.js";
6
6
  import { nodeTypes } from "@mdx-js/mdx";
7
7
  import { visit as estreeVisit } from "estree-util-visit";
8
- import { bold, yellow } from "kleur/colors";
9
- import { pathToFileURL } from "node:url";
10
8
  import rehypeRaw from "rehype-raw";
11
9
  import remarkGfm from "remark-gfm";
12
10
  import remarkSmartypants from "remark-smartypants";
13
- import { visit } from "unist-util-visit";
14
11
  import { rehypeInjectHeadingsExport } from "./rehype-collect-headings.js";
15
12
  import rehypeMetaString from "./rehype-meta-string.js";
13
+ import { rehypeOptimizeStatic } from "./rehype-optimize-static.js";
14
+ import { remarkImageToComponent } from "./remark-images-to-component.js";
16
15
  import remarkPrism from "./remark-prism.js";
17
16
  import remarkShiki from "./remark-shiki.js";
18
- import { isRelativePath, jsToTreeNode } from "./utils.js";
17
+ import { jsToTreeNode } from "./utils.js";
18
+ const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
19
19
  function recmaInjectImportMetaEnvPlugin({
20
20
  importMetaEnv
21
21
  }) {
@@ -43,6 +43,8 @@ function rehypeApplyFrontmatterExport() {
43
43
  const astroData = safelyGetAstroData(vfile.data);
44
44
  if (astroData instanceof InvalidAstroDataError)
45
45
  throw new Error(
46
+ // Copied from Astro core `errors-data`
47
+ // TODO: find way to import error data from core
46
48
  '[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`.'
47
49
  );
48
50
  const { frontmatter } = astroData;
@@ -52,6 +54,7 @@ function rehypeApplyFrontmatterExport() {
52
54
  if (frontmatter.layout) {
53
55
  exportNodes.unshift(
54
56
  jsToTreeNode(
57
+ /** @see 'vite-plugin-markdown' for layout props reference */
55
58
  `import { jsx as layoutJsx } from 'astro/jsx-runtime';
56
59
 
57
60
  export default async function ({ children }) {
@@ -59,22 +62,6 @@ function rehypeApplyFrontmatterExport() {
59
62
  const { layout, ...content } = frontmatter;
60
63
  content.file = file;
61
64
  content.url = url;
62
- content.astro = {};
63
- Object.defineProperty(content.astro, 'headings', {
64
- get() {
65
- throw new Error('The "astro" property is no longer supported! To access "headings" from your layout, try using "Astro.props.headings."')
66
- }
67
- });
68
- Object.defineProperty(content.astro, 'html', {
69
- get() {
70
- throw new Error('The "astro" property is no longer supported! To access "html" from your layout, try using "Astro.props.compiledContent()."')
71
- }
72
- });
73
- Object.defineProperty(content.astro, 'source', {
74
- get() {
75
- throw new Error('The "astro" property is no longer supported! To access "source" from your layout, try using "Astro.props.rawContent()."')
76
- }
77
- });
78
65
  return layoutJsx(Layout, {
79
66
  file,
80
67
  url,
@@ -91,79 +78,48 @@ function rehypeApplyFrontmatterExport() {
91
78
  tree.children = exportNodes.concat(tree.children);
92
79
  };
93
80
  }
94
- function toRemarkContentRelImageError({ srcDir }) {
95
- const contentDir = new URL("content/", srcDir);
96
- return function remarkContentRelImageError() {
97
- return (tree, vfile) => {
98
- const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href);
99
- if (!isContentFile)
100
- return;
101
- const relImagePaths = /* @__PURE__ */ new Set();
102
- visit(tree, "image", function raiseError(node) {
103
- if (isRelativePath(node.url)) {
104
- relImagePaths.add(node.url);
105
- }
106
- });
107
- if (relImagePaths.size === 0)
108
- return;
109
- const errorMessage = `Relative image paths are not supported in the content/ directory. Place local images in the public/ directory and use absolute paths (see https://docs.astro.build/en/guides/images/#in-markdown-files):
110
- ` + [...relImagePaths].map((path) => JSON.stringify(path)).join(",\n");
111
- throw new Error(errorMessage);
112
- };
113
- };
114
- }
115
- async function getRemarkPlugins(mdxOptions, config) {
116
- let remarkPlugins = [];
117
- if (mdxOptions.syntaxHighlight === "shiki") {
118
- remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
119
- }
120
- if (mdxOptions.syntaxHighlight === "prism") {
121
- remarkPlugins.push(remarkPrism);
122
- }
123
- if (mdxOptions.gfm) {
124
- remarkPlugins.push(remarkGfm);
81
+ async function getRemarkPlugins(mdxOptions) {
82
+ let remarkPlugins = [remarkCollectImages, remarkImageToComponent];
83
+ if (!isPerformanceBenchmark) {
84
+ if (mdxOptions.gfm) {
85
+ remarkPlugins.push(remarkGfm);
86
+ }
87
+ if (mdxOptions.smartypants) {
88
+ remarkPlugins.push(remarkSmartypants);
89
+ }
125
90
  }
126
- if (mdxOptions.smartypants) {
127
- remarkPlugins.push(remarkSmartypants);
91
+ remarkPlugins = [...remarkPlugins, ...mdxOptions.remarkPlugins];
92
+ if (!isPerformanceBenchmark) {
93
+ if (mdxOptions.syntaxHighlight === "shiki") {
94
+ remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
95
+ }
96
+ if (mdxOptions.syntaxHighlight === "prism") {
97
+ remarkPlugins.push(remarkPrism);
98
+ }
128
99
  }
129
- remarkPlugins = [...remarkPlugins, ...ignoreStringPlugins(mdxOptions.remarkPlugins)];
130
- remarkPlugins.push(toRemarkContentRelImageError(config));
131
100
  return remarkPlugins;
132
101
  }
133
102
  function getRehypePlugins(mdxOptions) {
134
103
  let rehypePlugins = [
104
+ // ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters
135
105
  rehypeMetaString,
106
+ // rehypeRaw allows custom syntax highlighters to work without added config
136
107
  [rehypeRaw, { passThrough: nodeTypes }]
137
108
  ];
138
109
  rehypePlugins = [
139
110
  ...rehypePlugins,
140
- ...ignoreStringPlugins(mdxOptions.rehypePlugins),
141
- rehypeHeadingIds,
142
- rehypeInjectHeadingsExport,
111
+ ...mdxOptions.rehypePlugins,
112
+ // getHeadings() is guaranteed by TS, so this must be included.
113
+ // We run `rehypeHeadingIds` _last_ to respect any custom IDs set by user plugins.
114
+ ...isPerformanceBenchmark ? [] : [rehypeHeadingIds, rehypeInjectHeadingsExport],
115
+ // computed from `astro.data.frontmatter` in VFile data
143
116
  rehypeApplyFrontmatterExport
144
117
  ];
145
- return rehypePlugins;
146
- }
147
- function ignoreStringPlugins(plugins) {
148
- let validPlugins = [];
149
- let hasInvalidPlugin = false;
150
- for (const plugin of plugins) {
151
- if (typeof plugin === "string") {
152
- console.warn(yellow(`[MDX] ${bold(plugin)} not applied.`));
153
- hasInvalidPlugin = true;
154
- } else if (Array.isArray(plugin) && typeof plugin[0] === "string") {
155
- console.warn(yellow(`[MDX] ${bold(plugin[0])} not applied.`));
156
- hasInvalidPlugin = true;
157
- } else {
158
- validPlugins.push(plugin);
159
- }
118
+ if (mdxOptions.optimize) {
119
+ const options = mdxOptions.optimize === true ? void 0 : mdxOptions.optimize;
120
+ rehypePlugins.push([rehypeOptimizeStatic, options]);
160
121
  }
161
- if (hasInvalidPlugin) {
162
- console.warn(
163
- `To inherit Markdown plugins in MDX, please use explicit imports in your config instead of "strings." See Markdown docs: https://docs.astro.build/en/guides/markdown-content/#markdown-plugins`
164
- );
165
- }
166
- return validPlugins;
122
+ return rehypePlugins;
167
123
  }
168
124
  function getImportMetaEnvVariableName(node) {
169
125
  try {
@@ -1,2 +1,2 @@
1
- import { MarkdownVFile } from '@astrojs/markdown-remark';
1
+ import type { MarkdownVFile } from '@astrojs/markdown-remark';
2
2
  export declare function rehypeInjectHeadingsExport(): (tree: any, file: MarkdownVFile) => void;
@@ -2,9 +2,8 @@ import { visit } from "unist-util-visit";
2
2
  function rehypeMetaString() {
3
3
  return function(tree) {
4
4
  visit(tree, (node) => {
5
- var _a;
6
- if (node.type === "element" && node.tagName === "code" && ((_a = node.data) == null ? void 0 : _a.meta)) {
7
- node.properties ?? (node.properties = {});
5
+ if (node.type === "element" && node.tagName === "code" && node.data?.meta) {
6
+ node.properties ??= {};
8
7
  node.properties.metastring = node.data.meta;
9
8
  }
10
9
  });
@@ -0,0 +1,11 @@
1
+ export interface OptimizeOptions {
2
+ customComponentNames?: string[];
3
+ }
4
+ /**
5
+ * For MDX only, collapse static subtrees of the hast into `set:html`. Subtrees
6
+ * do not include any MDX elements.
7
+ *
8
+ * This optimization reduces the JS output as more content are represented as a
9
+ * string instead, which also reduces the AST size that Rollup holds in memory.
10
+ */
11
+ export declare function rehypeOptimizeStatic(options?: OptimizeOptions): (tree: any) => void;