@astrojs/mdx 0.19.4 → 0.19.6

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/README.md CHANGED
@@ -83,6 +83,7 @@ You can configure how your MDX is rendered with the following options:
83
83
  - [Options inherited from Markdown config](#options-inherited-from-markdown-config)
84
84
  - [`extendMarkdownConfig`](#extendmarkdownconfig)
85
85
  - [`recmaPlugins`](#recmaplugins)
86
+ - [`optimize`](#optimize)
86
87
 
87
88
  ### Options inherited from Markdown config
88
89
 
@@ -183,6 +184,71 @@ These are plugins that modify the output [estree](https://github.com/estree/estr
183
184
 
184
185
  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.
185
186
 
187
+ ### `optimize`
188
+
189
+ - **Type:** `boolean | { customComponentNames?: string[] }`
190
+
191
+ 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.
192
+
193
+ This is disabled by default. To enable MDX optimization, add the following to your MDX integration configuration:
194
+
195
+ __`astro.config.mjs`__
196
+
197
+ ```js
198
+ import { defineConfig } from 'astro/config';
199
+ import mdx from '@astrojs/mdx';
200
+
201
+ export default defineConfig({
202
+ integrations: [
203
+ mdx({
204
+ optimize: true,
205
+ })
206
+ ]
207
+ });
208
+ ```
209
+
210
+ #### `customComponentNames`
211
+
212
+ - **Type:** `string[]`
213
+
214
+ 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).
215
+
216
+ 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.
217
+
218
+ For example, the intended MDX output of the following is `<Heading>...</Heading>` in place of every `"<h1>...</h1>"`:
219
+
220
+ ```astro
221
+ ---
222
+ import { Content, components } from '../content.mdx';
223
+ import Heading from '../Heading.astro';
224
+ ---
225
+
226
+ <Content components={{...components, h1: Heading }} />
227
+ ```
228
+
229
+ To configure optimization for this using the `customComponentNames` property, specify an array of HTML element names that should be treated as custom components:
230
+
231
+ __`astro.config.mjs`__
232
+
233
+ ```js
234
+ import { defineConfig } from 'astro/config';
235
+ import mdx from '@astrojs/mdx';
236
+
237
+ export default defineConfig({
238
+ integrations: [
239
+ mdx({
240
+ optimize: {
241
+ // Prevent the optimizer from handling `h1` elements
242
+ // These will be treated as custom components
243
+ customComponentNames: ['h1'],
244
+ },
245
+ })
246
+ ]
247
+ });
248
+ ```
249
+
250
+ 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.
251
+
186
252
  ## Examples
187
253
 
188
254
  * 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.
package/dist/index.d.ts CHANGED
@@ -2,11 +2,13 @@ import { markdownConfigDefaults } from '@astrojs/markdown-remark';
2
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
+ import type { OptimizeOptions } from './rehype-optimize-static.js';
5
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,7 +1,6 @@
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";
5
4
  import { parse as parseESM } from "es-module-lexer";
6
5
  import fs from "node:fs/promises";
7
6
  import { fileURLToPath } from "node:url";
@@ -30,7 +29,10 @@ function mdx(partialMdxOptions = {}) {
30
29
  contentModuleTypes: await fs.readFile(
31
30
  new URL("../template/content-module-types.d.ts", import.meta.url),
32
31
  "utf-8"
33
- )
32
+ ),
33
+ // MDX can import scripts and styles,
34
+ // so wrap all MDX files with script / style propagation checks
35
+ handlePropagation: true
34
36
  });
35
37
  const extendMarkdownConfig = partialMdxOptions.extendMarkdownConfig ?? defaultMdxOptions.extendMarkdownConfig;
36
38
  const mdxOptions = applyDefaultOptions({
@@ -57,8 +59,8 @@ function mdx(partialMdxOptions = {}) {
57
59
  vite: {
58
60
  plugins: [
59
61
  {
62
+ name: "@mdx-js/rollup",
60
63
  enforce: "pre",
61
- ...mdxPlugin(mdxPluginOpts),
62
64
  configResolved(resolved) {
63
65
  importMetaEnv = { ...importMetaEnv, ...resolved.env };
64
66
  },
@@ -66,7 +68,7 @@ function mdx(partialMdxOptions = {}) {
66
68
  // ex. inject layouts
67
69
  async transform(_, id) {
68
70
  var _a;
69
- if (!id.endsWith("mdx"))
71
+ if (!id.endsWith(".mdx"))
70
72
  return;
71
73
  const { fileId } = getFileInfo(id, config);
72
74
  const code = await fs.readFile(fileId, "utf-8");
@@ -153,7 +155,8 @@ function markdownConfigToMdxOptions(markdownConfig) {
153
155
  ...markdownConfig,
154
156
  remarkPlugins: ignoreStringPlugins(markdownConfig.remarkPlugins),
155
157
  rehypePlugins: ignoreStringPlugins(markdownConfig.rehypePlugins),
156
- remarkRehype: markdownConfig.remarkRehype ?? {}
158
+ remarkRehype: markdownConfig.remarkRehype ?? {},
159
+ optimize: false
157
160
  };
158
161
  }
159
162
  function applyDefaultOptions({
@@ -169,7 +172,8 @@ function applyDefaultOptions({
169
172
  smartypants: options.smartypants ?? defaults.smartypants,
170
173
  remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
171
174
  rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
172
- shikiConfig: options.shikiConfig ?? defaults.shikiConfig
175
+ shikiConfig: options.shikiConfig ?? defaults.shikiConfig,
176
+ optimize: options.optimize ?? defaults.optimize
173
177
  };
174
178
  }
175
179
  function escapeViteEnvReferences(code) {
package/dist/plugins.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
1
+ import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
2
2
  import type { AstroConfig } from 'astro';
3
3
  import type { VFile } from 'vfile';
4
4
  import type { MdxOptions } from './index.js';
@@ -6,5 +6,5 @@ export declare function recmaInjectImportMetaEnvPlugin({ importMetaEnv, }: {
6
6
  importMetaEnv: Record<string, any>;
7
7
  }): (tree: any) => void;
8
8
  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'];
9
+ export declare function getRemarkPlugins(mdxOptions: MdxOptions, config: AstroConfig): Promise<PluggableList>;
10
+ export declare function getRehypePlugins(mdxOptions: MdxOptions): PluggableList;
package/dist/plugins.js CHANGED
@@ -10,6 +10,7 @@ import remarkGfm from "remark-gfm";
10
10
  import remarkSmartypants from "remark-smartypants";
11
11
  import { rehypeInjectHeadingsExport } from "./rehype-collect-headings.js";
12
12
  import rehypeMetaString from "./rehype-meta-string.js";
13
+ import { rehypeOptimizeStatic } from "./rehype-optimize-static.js";
13
14
  import { remarkImageToComponent } from "./remark-images-to-component.js";
14
15
  import remarkPrism from "./remark-prism.js";
15
16
  import remarkShiki from "./remark-shiki.js";
@@ -116,6 +117,10 @@ function getRehypePlugins(mdxOptions) {
116
117
  // computed from `astro.data.frontmatter` in VFile data
117
118
  rehypeApplyFrontmatterExport
118
119
  ];
120
+ if (mdxOptions.optimize) {
121
+ const options = mdxOptions.optimize === true ? void 0 : mdxOptions.optimize;
122
+ rehypePlugins.push([rehypeOptimizeStatic, options]);
123
+ }
119
124
  return rehypePlugins;
120
125
  }
121
126
  function getImportMetaEnvVariableName(node) {
@@ -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;
@@ -0,0 +1,63 @@
1
+ import { visit } from "estree-util-visit";
2
+ import { toHtml } from "hast-util-to-html";
3
+ const exportConstComponentsRe = /export\s+const\s+components\s*=/;
4
+ function rehypeOptimizeStatic(options) {
5
+ return (tree) => {
6
+ var _a, _b, _c, _d, _e, _f;
7
+ const customComponentNames = new Set(options == null ? void 0 : options.customComponentNames);
8
+ for (const child of tree.children) {
9
+ if (child.type === "mdxjsEsm" && exportConstComponentsRe.test(child.value)) {
10
+ const objectPropertyNodes = (_d = (_c = (_b = (_a = child.data.estree.body[0]) == null ? void 0 : _a.declarations) == null ? void 0 : _b[0]) == null ? void 0 : _c.init) == null ? void 0 : _d.properties;
11
+ if (objectPropertyNodes) {
12
+ for (const objectPropertyNode of objectPropertyNodes) {
13
+ const componentName = ((_e = objectPropertyNode.key) == null ? void 0 : _e.name) ?? ((_f = objectPropertyNode.key) == null ? void 0 : _f.value);
14
+ if (componentName) {
15
+ customComponentNames.add(componentName);
16
+ }
17
+ }
18
+ }
19
+ }
20
+ }
21
+ const allPossibleElements = /* @__PURE__ */ new Set();
22
+ const elementStack = [];
23
+ visit(tree, {
24
+ enter(node) {
25
+ const isCustomComponent = node.tagName && customComponentNames.has(node.tagName);
26
+ if (node.type.startsWith("mdx") || isCustomComponent) {
27
+ for (const el of elementStack) {
28
+ allPossibleElements.delete(el);
29
+ }
30
+ elementStack.length = 0;
31
+ }
32
+ if (node.type === "element" || node.type === "mdxJsxFlowElement") {
33
+ elementStack.push(node);
34
+ allPossibleElements.add(node);
35
+ }
36
+ },
37
+ leave(node, _, __, parents) {
38
+ if (node.type === "element" || node.type === "mdxJsxFlowElement") {
39
+ elementStack.pop();
40
+ const parent = parents[parents.length - 1];
41
+ if (allPossibleElements.has(parent)) {
42
+ allPossibleElements.delete(node);
43
+ }
44
+ }
45
+ }
46
+ });
47
+ for (const el of allPossibleElements) {
48
+ if (el.type === "mdxJsxFlowElement") {
49
+ el.attributes.push({
50
+ type: "mdxJsxAttribute",
51
+ name: "set:html",
52
+ value: toHtml(el.children)
53
+ });
54
+ } else {
55
+ el.properties["set:html"] = toHtml(el.children);
56
+ }
57
+ el.children = [];
58
+ }
59
+ };
60
+ }
61
+ export {
62
+ rehypeOptimizeStatic
63
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/mdx",
3
3
  "description": "Add support for MDX pages in your Astro site",
4
- "version": "0.19.4",
4
+ "version": "0.19.6",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -30,12 +30,12 @@
30
30
  "@astrojs/markdown-remark": "^2.2.1",
31
31
  "@astrojs/prism": "^2.1.2",
32
32
  "@mdx-js/mdx": "^2.3.0",
33
- "@mdx-js/rollup": "^2.3.0",
34
33
  "acorn": "^8.8.0",
35
34
  "es-module-lexer": "^1.1.1",
36
35
  "estree-util-visit": "^1.2.0",
37
36
  "github-slugger": "^1.4.0",
38
37
  "gray-matter": "^4.0.3",
38
+ "hast-util-to-html": "^8.0.4",
39
39
  "kleur": "^4.1.4",
40
40
  "rehype-raw": "^6.1.1",
41
41
  "remark-frontmatter": "^4.0.1",
@@ -67,7 +67,7 @@
67
67
  "remark-shiki-twoslash": "^3.1.0",
68
68
  "remark-toc": "^8.0.1",
69
69
  "vite": "^4.3.1",
70
- "astro": "2.5.5",
70
+ "astro": "2.5.7",
71
71
  "astro-scripts": "0.0.14"
72
72
  },
73
73
  "engines": {