@astrojs/mdx 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +21 -3
  4. package/dist/astro-data-utils.d.ts +9 -0
  5. package/dist/astro-data-utils.js +66 -0
  6. package/dist/index.js +64 -47
  7. package/dist/rehype-collect-headings.d.ts +6 -0
  8. package/dist/rehype-collect-headings.js +47 -0
  9. package/dist/remark-prism.js +2 -34
  10. package/dist/utils.d.ts +5 -3
  11. package/dist/utils.js +22 -3
  12. package/package.json +9 -6
  13. package/src/astro-data-utils.ts +81 -0
  14. package/src/index.ts +83 -63
  15. package/src/rehype-collect-headings.ts +50 -0
  16. package/src/remark-prism.ts +2 -44
  17. package/src/utils.ts +27 -2
  18. package/test/fixtures/mdx-escape/src/components/Em.astro +7 -0
  19. package/test/fixtures/mdx-escape/src/components/P.astro +1 -0
  20. package/test/fixtures/mdx-escape/src/components/Title.astro +1 -0
  21. package/test/fixtures/mdx-escape/src/pages/html-tag.mdx +5 -0
  22. package/test/fixtures/mdx-escape/src/pages/index.mdx +13 -0
  23. package/test/fixtures/mdx-frontmatter/src/layouts/Base.astro +13 -3
  24. package/test/fixtures/mdx-frontmatter/src/pages/index.mdx +3 -0
  25. package/test/fixtures/mdx-frontmatter/src/pages/with-headings.mdx +7 -0
  26. package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +12 -0
  27. package/test/fixtures/mdx-frontmatter-injection/node_modules/.bin/astro +17 -0
  28. package/test/fixtures/mdx-frontmatter-injection/package.json +12 -0
  29. package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +20 -0
  30. package/test/fixtures/mdx-frontmatter-injection/src/pages/glob.json.js +6 -0
  31. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +3 -0
  32. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +19 -0
  33. package/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx +7 -0
  34. package/test/fixtures/mdx-get-headings/src/pages/pages.json.js +11 -0
  35. package/test/fixtures/mdx-get-headings/src/pages/test-with-jsx-expressions.mdx +8 -0
  36. package/test/fixtures/mdx-get-headings/src/pages/test.mdx +9 -0
  37. package/test/fixtures/mdx-page/astro.config.ts +5 -0
  38. package/test/fixtures/mdx-page/node_modules/.bin/astro +17 -0
  39. package/test/fixtures/mdx-page/package.json +7 -0
  40. package/test/fixtures/mdx-plus-react/astro.config.mjs +6 -0
  41. package/test/fixtures/mdx-plus-react/node_modules/.bin/astro +17 -0
  42. package/test/fixtures/mdx-plus-react/package.json +8 -0
  43. package/test/fixtures/mdx-plus-react/src/components/Component.jsx +5 -0
  44. package/test/fixtures/mdx-plus-react/src/pages/index.astro +11 -0
  45. package/test/fixtures/mdx-rehype-plugins/src/pages/reading-time.json.js +7 -0
  46. package/test/fixtures/mdx-rehype-plugins/src/pages/space-ipsum.mdx +25 -0
  47. package/test/fixtures/mdx-remark-plugins/src/pages/headings-glob.json.js +6 -0
  48. package/test/mdx-escape.test.js +32 -0
  49. package/test/mdx-frontmatter-injection.test.js +44 -0
  50. package/test/mdx-frontmatter.test.js +26 -26
  51. package/test/mdx-get-headings.test.js +60 -0
  52. package/test/mdx-page.test.js +0 -1
  53. package/test/mdx-plus-react.test.js +25 -0
  54. package/test/mdx-rehype-plugins.test.js +70 -0
@@ -1,5 +1,5 @@
1
- @astrojs/mdx:build: cache hit, replaying output c4afb89e8a455a28
2
- @astrojs/mdx:build: 
3
- @astrojs/mdx:build: > @astrojs/mdx@0.4.0 build /home/runner/work/astro/astro/packages/integrations/mdx
4
- @astrojs/mdx:build: > astro-scripts build "src/**/*.ts" && tsc
5
- @astrojs/mdx:build: 
1
+ @astrojs/mdx:build: cache hit, replaying output fbb8fed20a6e13e0
2
+ @astrojs/mdx:build: 
3
+ @astrojs/mdx:build: > @astrojs/mdx@0.7.0 build /home/runner/work/astro/astro/packages/integrations/mdx
4
+ @astrojs/mdx:build: > astro-scripts build "src/**/*.ts" && tsc
5
+ @astrojs/mdx:build: 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @astrojs/mdx
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#4176](https://github.com/withastro/astro/pull/4176) [`2675b8633`](https://github.com/withastro/astro/commit/2675b8633c5d5c45b237ec87940d5eaf1bfb1b4b) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Support frontmatter injection for MD and MDX using remark and rehype plugins
8
+
9
+ ### Patch Changes
10
+
11
+ - [#4181](https://github.com/withastro/astro/pull/4181) [`77cede720`](https://github.com/withastro/astro/commit/77cede720b09bce34f29c3d2d8b505311ce876b1) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Make collect-headings rehype plugin non-overridable
12
+
13
+ * [#4174](https://github.com/withastro/astro/pull/4174) [`8eb3a8c6d`](https://github.com/withastro/astro/commit/8eb3a8c6d9554707963c3a3bc36ed8b68d3cf0fb) Thanks [@matthewp](https://github.com/matthewp)! - Allows using React with automatic imports alongside MDX
14
+
15
+ - [#4145](https://github.com/withastro/astro/pull/4145) [`c7efcf57e`](https://github.com/withastro/astro/commit/c7efcf57e00a0fcde3bc9f813e3cc59902bd484c) Thanks [@FredKSchott](https://github.com/FredKSchott)! - Fix a missing newline bug when `layout` was set.
16
+
17
+ ## 0.6.0
18
+
19
+ ### Minor Changes
20
+
21
+ - [#4134](https://github.com/withastro/astro/pull/4134) [`2968ba2b6`](https://github.com/withastro/astro/commit/2968ba2b6f00775b6e9872681b390cb466fdbfa2) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Add `headings` and `frontmatter` properties to layout props
22
+
23
+ ## 0.5.0
24
+
25
+ ### Minor Changes
26
+
27
+ - [#4095](https://github.com/withastro/astro/pull/4095) [`40ef43a59`](https://github.com/withastro/astro/commit/40ef43a59b08a1a8fbcd9f4a53745a9636a4fbb9) Thanks [@bholmesdev](https://github.com/bholmesdev)! - Add IDs to MDX headings and expose via getHeadings() export
28
+
29
+ * [#4114](https://github.com/withastro/astro/pull/4114) [`64432bcb8`](https://github.com/withastro/astro/commit/64432bcb873efd0e4297c00fc9583a1fe516dfe7) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Refactor `@astrojs/mdx` and `@astrojs/markdown-remark` to use `@astrojs/prism` instead of duplicating the code
30
+
31
+ ### Patch Changes
32
+
33
+ - [#4112](https://github.com/withastro/astro/pull/4112) [`e33fc9bc4`](https://github.com/withastro/astro/commit/e33fc9bc46ff0a30013deb6dc76e545e70cc3a3e) Thanks [@matthewp](https://github.com/matthewp)! - Fix MDX working with a ts config file
34
+
35
+ * [#4049](https://github.com/withastro/astro/pull/4049) [`b60cc0538`](https://github.com/withastro/astro/commit/b60cc0538bc5c68dd411117780d20d892530789d) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve `injectScript` handling for non-Astro pages
36
+
37
+ * Updated dependencies [[`64432bcb8`](https://github.com/withastro/astro/commit/64432bcb873efd0e4297c00fc9583a1fe516dfe7)]:
38
+ - @astrojs/prism@0.7.0
39
+
3
40
  ## 0.4.0
4
41
 
5
42
  ### Minor Changes
package/README.md CHANGED
@@ -103,6 +103,24 @@ const posts = await Astro.glob('./*.mdx');
103
103
 
104
104
  See [the official "how MDX works" guide](https://mdxjs.com/docs/using-mdx/#how-mdx-works) for more on MDX variables.
105
105
 
106
+ ### Exported properties
107
+
108
+ Alongside your [MDX variable exports](#variables), we generate a few helpful exports as well. These are accessible when importing an MDX file via `import` statements or [`Astro.glob`](https://docs.astro.build/en/reference/api-reference/#astroglob).
109
+
110
+ #### `file`
111
+
112
+ The absolute path to the MDX file (e.g. `home/user/projects/.../file.md`).
113
+
114
+ #### `url`
115
+
116
+ The browser-ready URL for MDX files under `src/pages/`. For example, `src/pages/en/about.mdx` will provide a `url` of `/en/about/`. For MDX files outside of `src/pages`, `url` will be `undefined`.
117
+
118
+ #### `getHeadings()`
119
+
120
+ **Returns:** `{ depth: number; slug: string; text: string }[]`
121
+
122
+ A function that returns an array of all headings (i.e. `h1 -> h6` elements) in the MDX file. Each heading’s `slug` corresponds to the generated ID for a given heading and can be used for anchor links.
123
+
106
124
  ### Frontmatter
107
125
 
108
126
  Astro also supports YAML-based frontmatter out-of-the-box using the [remark-mdx-frontmatter](https://github.com/remcohaszing/remark-mdx-frontmatter) plugin. By default, all variables declared in a frontmatter fence (`---`) will be accessible via the `frontmatter` export. See the `frontmatterOptions` configuration to customize this behavior.
@@ -279,11 +297,11 @@ export default {
279
297
  <details>
280
298
  <summary><strong>rehypePlugins</strong></summary>
281
299
 
282
- **Default plugins:** none
283
-
284
300
  [Rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) allow you to transform the HTML that your Markdown generates. We recommend checking the [Remark plugin](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) catalog first _before_ considering rehype plugins, since most users want to transform their Markdown syntax instead. If HTML transforms are what you need, we encourage you to browse [awesome-rehype](https://github.com/rehypejs/awesome-rehype) for a full curated list of plugins!
285
301
 
286
- To apply rehype plugins, use the `rehypePlugins` configuration option like so:
302
+ We apply our own (non-overridable) [`collect-headings`](https://github.com/withastro/astro/blob/main/packages/integrations/mdx/src/rehype-collect-headings.ts) plugin. This applies IDs to all headings (i.e. `h1 -> h6`) in your MDX files to [link to headings via anchor tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#linking_to_an_element_on_the_same_page).
303
+
304
+ To apply additional rehype plugins, pass an array to the `rehypePlugins` option like so:
287
305
 
288
306
  ```js
289
307
  // astro.config.mjs
@@ -0,0 +1,9 @@
1
+ import type { MarkdownAstroData } from 'astro';
2
+ import type { Data, VFile } from 'vfile';
3
+ export declare function remarkInitializeAstroData(): (tree: any, vfile: VFile) => void;
4
+ export declare function rehypeApplyFrontmatterExport(pageFrontmatter: Record<string, any>, exportName?: string): (tree: any, vfile: VFile) => void;
5
+ /**
6
+ * Copied from markdown utils
7
+ * @see "vite-plugin-utils"
8
+ */
9
+ export declare function safelyGetAstroData(vfileData: Data): MarkdownAstroData;
@@ -0,0 +1,66 @@
1
+ import { name as isValidIdentifierName } from "estree-util-is-identifier-name";
2
+ import { jsToTreeNode } from "./utils.js";
3
+ function remarkInitializeAstroData() {
4
+ return function(tree, vfile) {
5
+ if (!vfile.data.astro) {
6
+ vfile.data.astro = { frontmatter: {} };
7
+ }
8
+ };
9
+ }
10
+ function rehypeApplyFrontmatterExport(pageFrontmatter, exportName = "frontmatter") {
11
+ return function(tree, vfile) {
12
+ if (!isValidIdentifierName(exportName)) {
13
+ throw new Error(
14
+ `[MDX] ${JSON.stringify(
15
+ exportName
16
+ )} is not a valid frontmatter export name! Make sure "frontmatterOptions.name" could be used as a JS export (i.e. "export const frontmatterName = ...")`
17
+ );
18
+ }
19
+ const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data);
20
+ const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter };
21
+ let exportNodes = [];
22
+ if (!exportName) {
23
+ exportNodes = Object.entries(frontmatter).map(([k, v]) => {
24
+ if (!isValidIdentifierName(k)) {
25
+ throw new Error(
26
+ `[MDX] A remark or rehype plugin tried to inject ${JSON.stringify(
27
+ k
28
+ )} as a top-level export, which is not a valid export name.`
29
+ );
30
+ }
31
+ return jsToTreeNode(`export const ${k} = ${JSON.stringify(v)};`);
32
+ });
33
+ } else {
34
+ exportNodes = [jsToTreeNode(`export const ${exportName} = ${JSON.stringify(frontmatter)};`)];
35
+ }
36
+ tree.children = exportNodes.concat(tree.children);
37
+ };
38
+ }
39
+ function isValidAstroData(obj) {
40
+ if (typeof obj === "object" && obj !== null && obj.hasOwnProperty("frontmatter")) {
41
+ const { frontmatter } = obj;
42
+ try {
43
+ JSON.stringify(frontmatter);
44
+ } catch {
45
+ return false;
46
+ }
47
+ return typeof frontmatter === "object" && frontmatter !== null;
48
+ }
49
+ return false;
50
+ }
51
+ function safelyGetAstroData(vfileData) {
52
+ const { astro } = vfileData;
53
+ if (!astro)
54
+ return { frontmatter: {} };
55
+ if (!isValidAstroData(astro)) {
56
+ throw Error(
57
+ `[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!`
58
+ );
59
+ }
60
+ return astro;
61
+ }
62
+ export {
63
+ rehypeApplyFrontmatterExport,
64
+ remarkInitializeAstroData,
65
+ safelyGetAstroData
66
+ };
package/dist/index.js CHANGED
@@ -1,92 +1,109 @@
1
- import { nodeTypes } from "@mdx-js/mdx";
1
+ import { compile as mdxCompile, nodeTypes } from "@mdx-js/mdx";
2
2
  import mdxPlugin from "@mdx-js/rollup";
3
3
  import { parse as parseESM } from "es-module-lexer";
4
4
  import rehypeRaw from "rehype-raw";
5
- import remarkFrontmatter from "remark-frontmatter";
6
5
  import remarkGfm from "remark-gfm";
7
- import remarkMdxFrontmatter from "remark-mdx-frontmatter";
8
6
  import remarkShikiTwoslash from "remark-shiki-twoslash";
9
7
  import remarkSmartypants from "remark-smartypants";
8
+ import { VFile } from "vfile";
9
+ import { rehypeApplyFrontmatterExport, remarkInitializeAstroData } from "./astro-data-utils.js";
10
+ import rehypeCollectHeadings from "./rehype-collect-headings.js";
10
11
  import remarkPrism from "./remark-prism.js";
11
- import { getFileInfo, getFrontmatter } from "./utils.js";
12
- const DEFAULT_REMARK_PLUGINS = [remarkGfm, remarkSmartypants];
12
+ import { getFileInfo, parseFrontmatter } from "./utils.js";
13
+ const DEFAULT_REMARK_PLUGINS = [
14
+ remarkGfm,
15
+ remarkSmartypants
16
+ ];
17
+ const DEFAULT_REHYPE_PLUGINS = [];
13
18
  function handleExtends(config, defaults = []) {
14
19
  if (Array.isArray(config))
15
20
  return config;
16
21
  return [...defaults, ...(config == null ? void 0 : config.extends) ?? []];
17
22
  }
23
+ function getRemarkPlugins(mdxOptions, config) {
24
+ let remarkPlugins = [
25
+ remarkInitializeAstroData,
26
+ ...handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS)
27
+ ];
28
+ if (config.markdown.syntaxHighlight === "shiki") {
29
+ const shikiTwoslash = remarkShikiTwoslash.default ?? remarkShikiTwoslash;
30
+ remarkPlugins.push([shikiTwoslash, config.markdown.shikiConfig]);
31
+ }
32
+ if (config.markdown.syntaxHighlight === "prism") {
33
+ remarkPlugins.push(remarkPrism);
34
+ }
35
+ return remarkPlugins;
36
+ }
37
+ function getRehypePlugins(mdxOptions, config) {
38
+ let rehypePlugins = handleExtends(mdxOptions.rehypePlugins, DEFAULT_REHYPE_PLUGINS);
39
+ if (config.markdown.syntaxHighlight === "shiki" || config.markdown.syntaxHighlight === "prism") {
40
+ rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
41
+ }
42
+ rehypePlugins.push(rehypeCollectHeadings);
43
+ return rehypePlugins;
44
+ }
18
45
  function mdx(mdxOptions = {}) {
19
46
  return {
20
47
  name: "@astrojs/mdx",
21
48
  hooks: {
22
49
  "astro:config:setup": ({ updateConfig, config, addPageExtension, command }) => {
23
50
  addPageExtension(".mdx");
24
- let remarkPlugins = handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS);
25
- let rehypePlugins = handleExtends(mdxOptions.rehypePlugins);
26
- if (config.markdown.syntaxHighlight === "shiki") {
27
- remarkPlugins.push([
28
- remarkShikiTwoslash.default,
29
- config.markdown.shikiConfig
30
- ]);
31
- rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
32
- }
33
- if (config.markdown.syntaxHighlight === "prism") {
34
- remarkPlugins.push(remarkPrism);
35
- rehypePlugins.push([rehypeRaw, { passThrough: nodeTypes }]);
36
- }
37
- remarkPlugins.push(remarkFrontmatter);
38
- remarkPlugins.push([
39
- remarkMdxFrontmatter,
40
- {
41
- name: "frontmatter",
42
- ...mdxOptions.frontmatterOptions
43
- }
44
- ]);
45
- const configuredMdxPlugin = mdxPlugin({
46
- remarkPlugins,
47
- rehypePlugins,
51
+ const mdxPluginOpts = {
52
+ remarkPlugins: getRemarkPlugins(mdxOptions, config),
53
+ rehypePlugins: getRehypePlugins(mdxOptions, config),
48
54
  jsx: true,
49
55
  jsxImportSource: "astro",
50
56
  format: "mdx",
51
57
  mdExtensions: []
52
- });
58
+ };
53
59
  updateConfig({
54
60
  vite: {
55
61
  plugins: [
56
62
  {
57
63
  enforce: "pre",
58
- ...configuredMdxPlugin,
64
+ ...mdxPlugin(mdxPluginOpts),
59
65
  async transform(code, id) {
60
- var _a, _b;
61
- if (!id.endsWith(".mdx"))
66
+ if (!id.endsWith("mdx"))
62
67
  return;
63
- const mdxPluginTransform = (_a = configuredMdxPlugin.transform) == null ? void 0 : _a.bind(this);
64
- if ((_b = mdxOptions.frontmatterOptions) == null ? void 0 : _b.parsers) {
65
- return mdxPluginTransform == null ? void 0 : mdxPluginTransform(code, id);
66
- }
67
- const frontmatter = getFrontmatter(code, id);
68
+ let { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
68
69
  if (frontmatter.layout) {
69
- const { layout, ...content } = frontmatter;
70
- code += `
70
+ const { layout, ...contentProp } = frontmatter;
71
+ pageContent += `
72
+
71
73
  export default async function({ children }) {
72
74
  const Layout = (await import(${JSON.stringify(
73
75
  frontmatter.layout
74
76
  )})).default;
75
- return <Layout content={${JSON.stringify(
76
- content
77
- )}}>{children}</Layout> }`;
77
+ const frontmatter=${JSON.stringify(
78
+ contentProp
79
+ )};
80
+ return <Layout frontmatter={frontmatter} content={frontmatter} headings={getHeadings()}>{children}</Layout> }`;
78
81
  }
79
- return mdxPluginTransform == null ? void 0 : mdxPluginTransform(code, id);
82
+ const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
83
+ ...mdxPluginOpts,
84
+ rehypePlugins: [
85
+ ...mdxPluginOpts.rehypePlugins ?? [],
86
+ () => {
87
+ var _a;
88
+ return rehypeApplyFrontmatterExport(
89
+ frontmatter,
90
+ (_a = mdxOptions.frontmatterOptions) == null ? void 0 : _a.name
91
+ );
92
+ }
93
+ ]
94
+ });
95
+ return {
96
+ code: String(compiled.value),
97
+ map: compiled.map
98
+ };
80
99
  }
81
100
  },
82
101
  {
83
- name: "@astrojs/mdx",
102
+ name: "@astrojs/mdx-postprocess",
84
103
  transform(code, id) {
85
104
  if (!id.endsWith(".mdx"))
86
105
  return;
87
106
  const [, moduleExports] = parseESM(code);
88
- code += `
89
- import "${"astro:scripts/page-ssr.js"}";`;
90
107
  const { fileUrl, fileId } = getFileInfo(id, config);
91
108
  if (!moduleExports.includes("url")) {
92
109
  code += `
@@ -0,0 +1,6 @@
1
+ export interface MarkdownHeading {
2
+ depth: number;
3
+ slug: string;
4
+ text: string;
5
+ }
6
+ export default function rehypeCollectHeadings(): (tree: any) => void;
@@ -0,0 +1,47 @@
1
+ import Slugger from "github-slugger";
2
+ import { visit } from "unist-util-visit";
3
+ import { jsToTreeNode } from "./utils.js";
4
+ function rehypeCollectHeadings() {
5
+ const slugger = new Slugger();
6
+ return function(tree) {
7
+ const headings = [];
8
+ visit(tree, (node) => {
9
+ if (node.type !== "element")
10
+ return;
11
+ const { tagName } = node;
12
+ if (tagName[0] !== "h")
13
+ return;
14
+ const [_, level] = tagName.match(/h([0-6])/) ?? [];
15
+ if (!level)
16
+ return;
17
+ const depth = Number.parseInt(level);
18
+ let text = "";
19
+ visit(node, (child, __, parent) => {
20
+ if (child.type === "element" || parent == null) {
21
+ return;
22
+ }
23
+ if (child.type === "raw" && child.value.match(/^\n?<.*>\n?$/)) {
24
+ return;
25
+ }
26
+ if ((/* @__PURE__ */ new Set(["text", "raw", "mdxTextExpression"])).has(child.type)) {
27
+ text += child.value;
28
+ }
29
+ });
30
+ node.properties = node.properties || {};
31
+ if (typeof node.properties.id !== "string") {
32
+ let slug = slugger.slug(text);
33
+ if (slug.endsWith("-")) {
34
+ slug = slug.slice(0, -1);
35
+ }
36
+ node.properties.id = slug;
37
+ }
38
+ headings.push({ depth, slug: node.properties.id, text });
39
+ });
40
+ tree.children.unshift(
41
+ jsToTreeNode(`export function getHeadings() { return ${JSON.stringify(headings)} }`)
42
+ );
43
+ };
44
+ }
45
+ export {
46
+ rehypeCollectHeadings as default
47
+ };
@@ -1,42 +1,10 @@
1
- import { addAstro } from "@astrojs/prism/internal";
2
- import Prism from "prismjs";
3
- import loadLanguages from "prismjs/components/index.js";
1
+ import { runHighlighterWithAstro } from "@astrojs/prism/dist/highlighter";
4
2
  import { visit } from "unist-util-visit";
5
- const languageMap = /* @__PURE__ */ new Map([["ts", "typescript"]]);
6
- function runHighlighter(lang, code) {
7
- let classLanguage = `language-${lang}`;
8
- if (lang == null) {
9
- lang = "plaintext";
10
- }
11
- const ensureLoaded = (language) => {
12
- if (language && !Prism.languages[language]) {
13
- loadLanguages([language]);
14
- }
15
- };
16
- if (languageMap.has(lang)) {
17
- ensureLoaded(languageMap.get(lang));
18
- } else if (lang === "astro") {
19
- ensureLoaded("typescript");
20
- addAstro(Prism);
21
- } else {
22
- ensureLoaded("markup-templating");
23
- ensureLoaded(lang);
24
- }
25
- if (lang && !Prism.languages[lang]) {
26
- console.warn(`Unable to load the language: ${lang}`);
27
- }
28
- const grammar = Prism.languages[lang];
29
- let html = code;
30
- if (grammar) {
31
- html = Prism.highlight(code, grammar, lang);
32
- }
33
- return { classLanguage, html };
34
- }
35
3
  function remarkPrism() {
36
4
  return (tree) => visit(tree, "code", (node) => {
37
5
  let { lang, value } = node;
38
6
  node.type = "html";
39
- let { html, classLanguage } = runHighlighter(lang, value);
7
+ let { html, classLanguage } = runHighlighterWithAstro(lang, value);
40
8
  let classes = [classLanguage];
41
9
  node.value = `<pre class="${classes.join(
42
10
  " "
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ import type { Options as AcornOpts } from 'acorn';
1
2
  import type { AstroConfig } from 'astro';
3
+ import type { MdxjsEsm } from 'mdast-util-mdx';
4
+ import matter from 'gray-matter';
2
5
  interface FileInfo {
3
6
  fileId: string;
4
7
  fileUrl: string;
@@ -9,7 +12,6 @@ export declare function getFileInfo(id: string, config: AstroConfig): FileInfo;
9
12
  * Match YAML exception handling from Astro core errors
10
13
  * @see 'astro/src/core/errors.ts'
11
14
  */
12
- export declare function getFrontmatter(code: string, id: string): {
13
- [key: string]: any;
14
- };
15
+ export declare function parseFrontmatter(code: string, id: string): matter.GrayMatterFile<string>;
16
+ export declare function jsToTreeNode(jsString: string, acornOpts?: AcornOpts): MdxjsEsm;
15
17
  export {};
package/dist/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { parse } from "acorn";
1
2
  import matter from "gray-matter";
2
3
  function appendForwardSlash(path) {
3
4
  return path.endsWith("/") ? path : path + "/";
@@ -26,9 +27,9 @@ function getFileInfo(id, config) {
26
27
  }
27
28
  return { fileId, fileUrl };
28
29
  }
29
- function getFrontmatter(code, id) {
30
+ function parseFrontmatter(code, id) {
30
31
  try {
31
- return matter(code).data;
32
+ return matter(code);
32
33
  } catch (e) {
33
34
  if (e.name === "YAMLException") {
34
35
  const err = e;
@@ -41,7 +42,25 @@ function getFrontmatter(code, id) {
41
42
  }
42
43
  }
43
44
  }
45
+ function jsToTreeNode(jsString, acornOpts = {
46
+ ecmaVersion: "latest",
47
+ sourceType: "module"
48
+ }) {
49
+ return {
50
+ type: "mdxjsEsm",
51
+ value: "",
52
+ data: {
53
+ estree: {
54
+ body: [],
55
+ ...parse(jsString, acornOpts),
56
+ type: "Program",
57
+ sourceType: "module"
58
+ }
59
+ }
60
+ };
61
+ }
44
62
  export {
45
63
  getFileInfo,
46
- getFrontmatter
64
+ jsToTreeNode,
65
+ parseFrontmatter
47
66
  };
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.0",
4
+ "version": "0.7.0",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -14,7 +14,6 @@
14
14
  "keywords": [
15
15
  "astro-integration",
16
16
  "astro-component",
17
- "renderer",
18
17
  "mdx"
19
18
  ],
20
19
  "bugs": "https://github.com/withastro/astro/issues",
@@ -24,12 +23,13 @@
24
23
  "./package.json": "./package.json"
25
24
  },
26
25
  "dependencies": {
27
- "@astrojs/prism": "^0.6.1",
26
+ "@astrojs/prism": "^0.7.0",
28
27
  "@mdx-js/mdx": "^2.1.2",
29
28
  "@mdx-js/rollup": "^2.1.1",
29
+ "acorn": "^8.8.0",
30
30
  "es-module-lexer": "^0.10.5",
31
+ "github-slugger": "^1.4.0",
31
32
  "gray-matter": "^4.0.3",
32
- "prismjs": "^1.28.0",
33
33
  "rehype-raw": "^6.1.1",
34
34
  "remark-frontmatter": "^4.0.1",
35
35
  "remark-gfm": "^3.0.1",
@@ -37,17 +37,20 @@
37
37
  "remark-shiki-twoslash": "^3.1.0",
38
38
  "remark-smartypants": "^2.0.0",
39
39
  "shiki": "^0.10.1",
40
- "unist-util-visit": "^4.1.0"
40
+ "unist-util-visit": "^4.1.0",
41
+ "vfile": "^5.3.2"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/chai": "^4.3.1",
44
45
  "@types/mocha": "^9.1.1",
45
46
  "@types/yargs-parser": "^21.0.0",
46
- "astro": "1.0.0-rc.3",
47
+ "astro": "1.0.0-rc.7",
47
48
  "astro-scripts": "0.0.6",
48
49
  "chai": "^4.3.6",
49
50
  "linkedom": "^0.14.12",
51
+ "mdast-util-to-string": "^3.1.0",
50
52
  "mocha": "^9.2.2",
53
+ "reading-time": "^1.5.0",
51
54
  "remark-toc": "^8.0.1"
52
55
  },
53
56
  "engines": {
@@ -0,0 +1,81 @@
1
+ import type { MarkdownAstroData } from 'astro';
2
+ import { name as isValidIdentifierName } from 'estree-util-is-identifier-name';
3
+ import type { MdxjsEsm } from 'mdast-util-mdx';
4
+ import type { Data, VFile } from 'vfile';
5
+ import { jsToTreeNode } from './utils.js';
6
+
7
+ export function remarkInitializeAstroData() {
8
+ return function (tree: any, vfile: VFile) {
9
+ if (!vfile.data.astro) {
10
+ vfile.data.astro = { frontmatter: {} };
11
+ }
12
+ };
13
+ }
14
+
15
+ export function rehypeApplyFrontmatterExport(
16
+ pageFrontmatter: Record<string, any>,
17
+ exportName = 'frontmatter'
18
+ ) {
19
+ return function (tree: any, vfile: VFile) {
20
+ if (!isValidIdentifierName(exportName)) {
21
+ throw new Error(
22
+ `[MDX] ${JSON.stringify(
23
+ exportName
24
+ )} is not a valid frontmatter export name! Make sure "frontmatterOptions.name" could be used as a JS export (i.e. "export const frontmatterName = ...")`
25
+ );
26
+ }
27
+ const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data);
28
+ const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter };
29
+ let exportNodes: MdxjsEsm[] = [];
30
+ if (!exportName) {
31
+ exportNodes = Object.entries(frontmatter).map(([k, v]) => {
32
+ if (!isValidIdentifierName(k)) {
33
+ throw new Error(
34
+ `[MDX] A remark or rehype plugin tried to inject ${JSON.stringify(
35
+ k
36
+ )} as a top-level export, which is not a valid export name.`
37
+ );
38
+ }
39
+ return jsToTreeNode(`export const ${k} = ${JSON.stringify(v)};`);
40
+ });
41
+ } else {
42
+ exportNodes = [jsToTreeNode(`export const ${exportName} = ${JSON.stringify(frontmatter)};`)];
43
+ }
44
+ tree.children = exportNodes.concat(tree.children);
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Copied from markdown utils
50
+ * @see "vite-plugin-utils"
51
+ */
52
+ function isValidAstroData(obj: unknown): obj is MarkdownAstroData {
53
+ if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) {
54
+ const { frontmatter } = obj as any;
55
+ try {
56
+ // ensure frontmatter is JSON-serializable
57
+ JSON.stringify(frontmatter);
58
+ } catch {
59
+ return false;
60
+ }
61
+ return typeof frontmatter === 'object' && frontmatter !== null;
62
+ }
63
+ return false;
64
+ }
65
+
66
+ /**
67
+ * Copied from markdown utils
68
+ * @see "vite-plugin-utils"
69
+ */
70
+ export function safelyGetAstroData(vfileData: Data): MarkdownAstroData {
71
+ const { astro } = vfileData;
72
+
73
+ if (!astro) return { frontmatter: {} };
74
+ if (!isValidAstroData(astro)) {
75
+ throw Error(
76
+ `[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!`
77
+ );
78
+ }
79
+
80
+ return astro;
81
+ }