@astrojs/mdx 0.19.1 → 0.19.2

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 (139) hide show
  1. package/package.json +8 -4
  2. package/.turbo/turbo-build.log +0 -4
  3. package/CHANGELOG.md +0 -725
  4. package/src/index.ts +0 -226
  5. package/src/plugins.ts +0 -185
  6. package/src/rehype-collect-headings.ts +0 -11
  7. package/src/rehype-meta-string.ts +0 -17
  8. package/src/remark-images-to-component.ts +0 -98
  9. package/src/remark-prism.ts +0 -18
  10. package/src/remark-shiki.ts +0 -115
  11. package/src/utils.ts +0 -108
  12. package/test/css-head-mdx.test.js +0 -98
  13. package/test/fixtures/css-head-mdx/node_modules/.bin/astro +0 -17
  14. package/test/fixtures/css-head-mdx/package.json +0 -10
  15. package/test/fixtures/css-head-mdx/src/components/BaseHead.astro +0 -11
  16. package/test/fixtures/css-head-mdx/src/components/BasicBlock.astro +0 -14
  17. package/test/fixtures/css-head-mdx/src/components/GenericComponent.astro +0 -1
  18. package/test/fixtures/css-head-mdx/src/components/HelloWorld.astro +0 -11
  19. package/test/fixtures/css-head-mdx/src/components/MDXWrapper.astro +0 -9
  20. package/test/fixtures/css-head-mdx/src/components/P.astro +0 -3
  21. package/test/fixtures/css-head-mdx/src/components/SmallCaps.astro +0 -3
  22. package/test/fixtures/css-head-mdx/src/components/UsingMdx.astro +0 -8
  23. package/test/fixtures/css-head-mdx/src/components/WithHoistedScripts.astro +0 -6
  24. package/test/fixtures/css-head-mdx/src/content/blog/_styles.css +0 -3
  25. package/test/fixtures/css-head-mdx/src/content/blog/using-mdx.mdx +0 -6
  26. package/test/fixtures/css-head-mdx/src/content/posts/test.mdx +0 -5
  27. package/test/fixtures/css-head-mdx/src/content/posts/using-component.mdx +0 -13
  28. package/test/fixtures/css-head-mdx/src/layouts/ContentLayout.astro +0 -18
  29. package/test/fixtures/css-head-mdx/src/layouts/DocumentLayout.astro +0 -15
  30. package/test/fixtures/css-head-mdx/src/layouts/One.astro +0 -15
  31. package/test/fixtures/css-head-mdx/src/layouts/Three.astro +0 -6
  32. package/test/fixtures/css-head-mdx/src/layouts/Two.astro +0 -6
  33. package/test/fixtures/css-head-mdx/src/pages/DirectContentUsage.astro +0 -17
  34. package/test/fixtures/css-head-mdx/src/pages/componentwithtext.mdx +0 -12
  35. package/test/fixtures/css-head-mdx/src/pages/indexOne.astro +0 -10
  36. package/test/fixtures/css-head-mdx/src/pages/indexThree.astro +0 -10
  37. package/test/fixtures/css-head-mdx/src/pages/indexTwo.astro +0 -10
  38. package/test/fixtures/css-head-mdx/src/pages/noLayoutWithComponent.mdx +0 -22
  39. package/test/fixtures/css-head-mdx/src/pages/posts/[post].astro +0 -18
  40. package/test/fixtures/css-head-mdx/src/pages/remote.astro +0 -17
  41. package/test/fixtures/css-head-mdx/src/pages/testOne.mdx +0 -15
  42. package/test/fixtures/css-head-mdx/src/pages/testThree.mdx +0 -15
  43. package/test/fixtures/css-head-mdx/src/pages/testTwo.mdx +0 -15
  44. package/test/fixtures/css-head-mdx/src/styles/global.css +0 -3
  45. package/test/fixtures/css-head-mdx/src/test.mdx +0 -14
  46. package/test/fixtures/mdx-astro-markdown-remarkRehype/src/pages/index.mdx +0 -5
  47. package/test/fixtures/mdx-component/src/components/Test.mdx +0 -3
  48. package/test/fixtures/mdx-component/src/components/WithFragment.mdx +0 -3
  49. package/test/fixtures/mdx-component/src/pages/glob.astro +0 -20
  50. package/test/fixtures/mdx-component/src/pages/index.astro +0 -5
  51. package/test/fixtures/mdx-component/src/pages/w-fragment.astro +0 -5
  52. package/test/fixtures/mdx-escape/src/components/Em.astro +0 -7
  53. package/test/fixtures/mdx-escape/src/components/P.astro +0 -1
  54. package/test/fixtures/mdx-escape/src/components/Title.astro +0 -1
  55. package/test/fixtures/mdx-escape/src/pages/html-tag.mdx +0 -5
  56. package/test/fixtures/mdx-escape/src/pages/index.mdx +0 -13
  57. package/test/fixtures/mdx-frontmatter/src/layouts/Base.astro +0 -38
  58. package/test/fixtures/mdx-frontmatter/src/pages/glob.json.js +0 -9
  59. package/test/fixtures/mdx-frontmatter/src/pages/index.mdx +0 -10
  60. package/test/fixtures/mdx-frontmatter/src/pages/with-headings.mdx +0 -7
  61. package/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +0 -12
  62. package/test/fixtures/mdx-frontmatter-injection/node_modules/.bin/astro +0 -17
  63. package/test/fixtures/mdx-frontmatter-injection/package.json +0 -12
  64. package/test/fixtures/mdx-frontmatter-injection/src/layouts/Base.astro +0 -17
  65. package/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +0 -27
  66. package/test/fixtures/mdx-frontmatter-injection/src/pages/glob.json.js +0 -6
  67. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +0 -8
  68. package/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +0 -24
  69. package/test/fixtures/mdx-get-headings/src/pages/pages.json.js +0 -11
  70. package/test/fixtures/mdx-get-headings/src/pages/test-with-frontmatter.mdx +0 -45
  71. package/test/fixtures/mdx-get-headings/src/pages/test-with-jsx-expressions.mdx +0 -8
  72. package/test/fixtures/mdx-get-headings/src/pages/test.mdx +0 -9
  73. package/test/fixtures/mdx-get-static-paths/src/content/1.mdx +0 -5
  74. package/test/fixtures/mdx-get-static-paths/src/pages/[slug].astro +0 -34
  75. package/test/fixtures/mdx-images/astro.config.ts +0 -8
  76. package/test/fixtures/mdx-images/node_modules/.bin/astro +0 -17
  77. package/test/fixtures/mdx-images/package.json +0 -10
  78. package/test/fixtures/mdx-images/src/assets/houston in space.webp +0 -0
  79. package/test/fixtures/mdx-images/src/assets/houston.webp +0 -0
  80. package/test/fixtures/mdx-images/src/pages/index.mdx +0 -11
  81. package/test/fixtures/mdx-infinite-loop/astro.config.ts +0 -6
  82. package/test/fixtures/mdx-infinite-loop/node_modules/.bin/astro +0 -17
  83. package/test/fixtures/mdx-infinite-loop/package.json +0 -11
  84. package/test/fixtures/mdx-infinite-loop/src/components/Test.js +0 -3
  85. package/test/fixtures/mdx-infinite-loop/src/pages/doc.mdx +0 -6
  86. package/test/fixtures/mdx-infinite-loop/src/pages/index.astro +0 -5
  87. package/test/fixtures/mdx-math/src/pages/mathjax.mdx +0 -5
  88. package/test/fixtures/mdx-namespace/astro.config.mjs +0 -6
  89. package/test/fixtures/mdx-namespace/node_modules/.bin/astro +0 -17
  90. package/test/fixtures/mdx-namespace/package.json +0 -11
  91. package/test/fixtures/mdx-namespace/src/components/Component.jsx +0 -6
  92. package/test/fixtures/mdx-namespace/src/pages/object.mdx +0 -3
  93. package/test/fixtures/mdx-namespace/src/pages/star.mdx +0 -3
  94. package/test/fixtures/mdx-page/astro.config.ts +0 -5
  95. package/test/fixtures/mdx-page/node_modules/.bin/astro +0 -17
  96. package/test/fixtures/mdx-page/package.json +0 -10
  97. package/test/fixtures/mdx-page/src/pages/index.mdx +0 -3
  98. package/test/fixtures/mdx-page/src/styles.css +0 -3
  99. package/test/fixtures/mdx-plugins/src/pages/with-plugins.mdx +0 -25
  100. package/test/fixtures/mdx-plus-react/astro.config.mjs +0 -6
  101. package/test/fixtures/mdx-plus-react/node_modules/.bin/astro +0 -17
  102. package/test/fixtures/mdx-plus-react/package.json +0 -11
  103. package/test/fixtures/mdx-plus-react/src/components/Component.jsx +0 -5
  104. package/test/fixtures/mdx-plus-react/src/pages/index.astro +0 -11
  105. package/test/fixtures/mdx-script-style-raw/src/pages/index.mdx +0 -13
  106. package/test/fixtures/mdx-slots/src/components/Slotted.astro +0 -4
  107. package/test/fixtures/mdx-slots/src/components/Test.mdx +0 -15
  108. package/test/fixtures/mdx-slots/src/pages/glob.astro +0 -11
  109. package/test/fixtures/mdx-slots/src/pages/index.astro +0 -5
  110. package/test/fixtures/mdx-syntax-hightlighting/src/pages/index.mdx +0 -9
  111. package/test/fixtures/mdx-url-export/src/pages/pages.json.js +0 -9
  112. package/test/fixtures/mdx-url-export/src/pages/test-1.mdx +0 -1
  113. package/test/fixtures/mdx-url-export/src/pages/test-2.mdx +0 -1
  114. package/test/fixtures/mdx-url-export/src/pages/with-url-override.mdx +0 -3
  115. package/test/fixtures/mdx-vite-env-vars/astro.config.mjs +0 -9
  116. package/test/fixtures/mdx-vite-env-vars/node_modules/.bin/astro +0 -17
  117. package/test/fixtures/mdx-vite-env-vars/package.json +0 -8
  118. package/test/fixtures/mdx-vite-env-vars/src/pages/frontmatter.json.js +0 -7
  119. package/test/fixtures/mdx-vite-env-vars/src/pages/vite-env-vars.mdx +0 -38
  120. package/test/mdx-astro-markdown-remarkRehype.test.js +0 -85
  121. package/test/mdx-component.test.js +0 -191
  122. package/test/mdx-escape.test.js +0 -32
  123. package/test/mdx-frontmatter-injection.test.js +0 -53
  124. package/test/mdx-frontmatter.test.js +0 -77
  125. package/test/mdx-get-headings.test.js +0 -194
  126. package/test/mdx-get-static-paths.test.js +0 -32
  127. package/test/mdx-images.test.js +0 -40
  128. package/test/mdx-infinite-loop.test.js +0 -30
  129. package/test/mdx-math.test.js +0 -64
  130. package/test/mdx-namespace.test.js +0 -83
  131. package/test/mdx-page.test.js +0 -64
  132. package/test/mdx-plugins.test.js +0 -285
  133. package/test/mdx-plus-react.test.js +0 -25
  134. package/test/mdx-script-style-raw.test.js +0 -70
  135. package/test/mdx-slots.js +0 -124
  136. package/test/mdx-syntax-highlighting.test.js +0 -145
  137. package/test/mdx-url-export.test.js +0 -28
  138. package/test/mdx-vite-env-vars.test.js +0 -54
  139. package/tsconfig.json +0 -10
package/src/index.ts DELETED
@@ -1,226 +0,0 @@
1
- import { markdownConfigDefaults } from '@astrojs/markdown-remark';
2
- import { toRemarkInitializeAstroData } from '@astrojs/markdown-remark/dist/internal.js';
3
- import { compile as mdxCompile } from '@mdx-js/mdx';
4
- import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
5
- import mdxPlugin, { type Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
6
- import type { AstroIntegration, ContentEntryType, HookParameters } from 'astro';
7
- import { parse as parseESM } from 'es-module-lexer';
8
- import fs from 'node:fs/promises';
9
- import { fileURLToPath } from 'node:url';
10
- import type { Options as RemarkRehypeOptions } from 'remark-rehype';
11
- import { SourceMapGenerator } from 'source-map';
12
- import { VFile } from 'vfile';
13
- import type { Plugin as VitePlugin } from 'vite';
14
- import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from './plugins.js';
15
- import { getFileInfo, ignoreStringPlugins, parseFrontmatter } from './utils.js';
16
-
17
- export type MdxOptions = Omit<typeof markdownConfigDefaults, 'remarkPlugins' | 'rehypePlugins'> & {
18
- extendMarkdownConfig: boolean;
19
- recmaPlugins: PluggableList;
20
- // Markdown allows strings as remark and rehype plugins.
21
- // This is not supported by the MDX compiler, so override types here.
22
- remarkPlugins: PluggableList;
23
- rehypePlugins: PluggableList;
24
- remarkRehype: RemarkRehypeOptions;
25
- };
26
-
27
- type SetupHookParams = HookParameters<'astro:config:setup'> & {
28
- // `addPageExtension` and `contentEntryType` are not a public APIs
29
- // Add type defs here
30
- addPageExtension: (extension: string) => void;
31
- addContentEntryType: (contentEntryType: ContentEntryType) => void;
32
- };
33
-
34
- export default function mdx(partialMdxOptions: Partial<MdxOptions> = {}): AstroIntegration {
35
- return {
36
- name: '@astrojs/mdx',
37
- hooks: {
38
- 'astro:config:setup': async (params) => {
39
- const { updateConfig, config, addPageExtension, addContentEntryType, command } =
40
- params as SetupHookParams;
41
-
42
- addPageExtension('.mdx');
43
- addContentEntryType({
44
- extensions: ['.mdx'],
45
- async getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
46
- const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
47
- return {
48
- data: parsed.data,
49
- body: parsed.content,
50
- slug: parsed.data.slug,
51
- rawData: parsed.matter,
52
- };
53
- },
54
- contentModuleTypes: await fs.readFile(
55
- new URL('../template/content-module-types.d.ts', import.meta.url),
56
- 'utf-8'
57
- ),
58
- });
59
-
60
- const extendMarkdownConfig =
61
- partialMdxOptions.extendMarkdownConfig ?? defaultMdxOptions.extendMarkdownConfig;
62
-
63
- const mdxOptions = applyDefaultOptions({
64
- options: partialMdxOptions,
65
- defaults: markdownConfigToMdxOptions(
66
- extendMarkdownConfig ? config.markdown : markdownConfigDefaults
67
- ),
68
- });
69
-
70
- const mdxPluginOpts: MdxRollupPluginOptions = {
71
- remarkPlugins: await getRemarkPlugins(mdxOptions, config),
72
- rehypePlugins: getRehypePlugins(mdxOptions),
73
- recmaPlugins: mdxOptions.recmaPlugins,
74
- remarkRehypeOptions: mdxOptions.remarkRehype,
75
- jsx: true,
76
- jsxImportSource: 'astro',
77
- // Note: disable `.md` (and other alternative extensions for markdown files like `.markdown`) support
78
- format: 'mdx',
79
- mdExtensions: [],
80
- };
81
-
82
- let importMetaEnv: Record<string, any> = {
83
- SITE: config.site,
84
- };
85
-
86
- updateConfig({
87
- vite: {
88
- plugins: [
89
- {
90
- enforce: 'pre',
91
- ...mdxPlugin(mdxPluginOpts),
92
- configResolved(resolved) {
93
- importMetaEnv = { ...importMetaEnv, ...resolved.env };
94
- },
95
- // Override transform to alter code before MDX compilation
96
- // ex. inject layouts
97
- async transform(_, id) {
98
- if (!id.endsWith('mdx')) return;
99
-
100
- // Read code from file manually to prevent Vite from parsing `import.meta.env` expressions
101
- const { fileId } = getFileInfo(id, config);
102
- const code = await fs.readFile(fileId, 'utf-8');
103
-
104
- const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
105
- const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
106
- ...mdxPluginOpts,
107
- elementAttributeNameCase: 'html',
108
- remarkPlugins: [
109
- // Ensure `data.astro` is available to all remark plugins
110
- toRemarkInitializeAstroData({ userFrontmatter: frontmatter }),
111
- ...(mdxPluginOpts.remarkPlugins ?? []),
112
- ],
113
- recmaPlugins: [
114
- ...(mdxPluginOpts.recmaPlugins ?? []),
115
- () => recmaInjectImportMetaEnvPlugin({ importMetaEnv }),
116
- ],
117
- SourceMapGenerator: config.vite.build?.sourcemap
118
- ? SourceMapGenerator
119
- : undefined,
120
- });
121
-
122
- return {
123
- code: escapeViteEnvReferences(String(compiled.value)),
124
- map: compiled.map,
125
- };
126
- },
127
- },
128
- {
129
- name: '@astrojs/mdx-postprocess',
130
- // These transforms must happen *after* JSX runtime transformations
131
- transform(code, id) {
132
- if (!id.endsWith('.mdx')) return;
133
-
134
- const [moduleImports, moduleExports] = parseESM(code);
135
-
136
- // Fragment import should already be injected, but check just to be safe.
137
- const importsFromJSXRuntime = moduleImports
138
- .filter(({ n }) => n === 'astro/jsx-runtime')
139
- .map(({ ss, se }) => code.substring(ss, se));
140
- const hasFragmentImport = importsFromJSXRuntime.some((statement) =>
141
- /[\s,{](Fragment,|Fragment\s*})/.test(statement)
142
- );
143
- if (!hasFragmentImport) {
144
- code = 'import { Fragment } from "astro/jsx-runtime"\n' + code;
145
- }
146
-
147
- const { fileUrl, fileId } = getFileInfo(id, config);
148
- if (!moduleExports.find(({ n }) => n === 'url')) {
149
- code += `\nexport const url = ${JSON.stringify(fileUrl)};`;
150
- }
151
- if (!moduleExports.find(({ n }) => n === 'file')) {
152
- code += `\nexport const file = ${JSON.stringify(fileId)};`;
153
- }
154
- if (!moduleExports.find(({ n }) => n === 'Content')) {
155
- // Make `Content` the default export so we can wrap `MDXContent` and pass in `Fragment`
156
- code = code.replace('export default MDXContent;', '');
157
- code += `\nexport const Content = (props = {}) => MDXContent({
158
- ...props,
159
- components: { Fragment, ...props.components },
160
- });
161
- export default Content;`;
162
- }
163
-
164
- // Ensures styles and scripts are injected into a `<head>`
165
- // When a layout is not applied
166
- code += `\nContent[Symbol.for('astro.needsHeadRendering')] = !Boolean(frontmatter.layout);`;
167
- code += `\nContent.moduleId = ${JSON.stringify(id)};`;
168
-
169
- if (command === 'dev') {
170
- // TODO: decline HMR updates until we have a stable approach
171
- code += `\nif (import.meta.hot) {
172
- import.meta.hot.decline();
173
- }`;
174
- }
175
- return { code: escapeViteEnvReferences(code), map: null };
176
- },
177
- },
178
- ] as VitePlugin[],
179
- },
180
- });
181
- },
182
- },
183
- };
184
- }
185
-
186
- const defaultMdxOptions = {
187
- extendMarkdownConfig: true,
188
- recmaPlugins: [],
189
- };
190
-
191
- function markdownConfigToMdxOptions(markdownConfig: typeof markdownConfigDefaults): MdxOptions {
192
- return {
193
- ...defaultMdxOptions,
194
- ...markdownConfig,
195
- remarkPlugins: ignoreStringPlugins(markdownConfig.remarkPlugins),
196
- rehypePlugins: ignoreStringPlugins(markdownConfig.rehypePlugins),
197
- remarkRehype: (markdownConfig.remarkRehype as any) ?? {},
198
- };
199
- }
200
-
201
- function applyDefaultOptions({
202
- options,
203
- defaults,
204
- }: {
205
- options: Partial<MdxOptions>;
206
- defaults: MdxOptions;
207
- }): MdxOptions {
208
- return {
209
- syntaxHighlight: options.syntaxHighlight ?? defaults.syntaxHighlight,
210
- extendMarkdownConfig: options.extendMarkdownConfig ?? defaults.extendMarkdownConfig,
211
- recmaPlugins: options.recmaPlugins ?? defaults.recmaPlugins,
212
- remarkRehype: options.remarkRehype ?? defaults.remarkRehype,
213
- gfm: options.gfm ?? defaults.gfm,
214
- smartypants: options.smartypants ?? defaults.smartypants,
215
- remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins,
216
- rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins,
217
- shikiConfig: options.shikiConfig ?? defaults.shikiConfig,
218
- };
219
- }
220
-
221
- // Converts the first dot in `import.meta.env` to its Unicode escape sequence,
222
- // which prevents Vite from replacing strings like `import.meta.env.SITE`
223
- // in our JS representation of loaded Markdown files
224
- function escapeViteEnvReferences(code: string) {
225
- return code.replace(/import\.meta\.env/g, 'import\\u002Emeta.env');
226
- }
package/src/plugins.ts DELETED
@@ -1,185 +0,0 @@
1
- import { rehypeHeadingIds, remarkCollectImages } from '@astrojs/markdown-remark';
2
- import {
3
- InvalidAstroDataError,
4
- safelyGetAstroData,
5
- } from '@astrojs/markdown-remark/dist/internal.js';
6
- import { nodeTypes } from '@mdx-js/mdx';
7
- import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
8
- import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
9
- import type { AstroConfig } from 'astro';
10
- import type { Literal, MemberExpression } from 'estree';
11
- import { visit as estreeVisit } from 'estree-util-visit';
12
- import rehypeRaw from 'rehype-raw';
13
- import remarkGfm from 'remark-gfm';
14
- import remarkSmartypants from 'remark-smartypants';
15
- import type { VFile } from 'vfile';
16
- import type { MdxOptions } from './index.js';
17
- import { rehypeInjectHeadingsExport } from './rehype-collect-headings.js';
18
- import rehypeMetaString from './rehype-meta-string.js';
19
- import { remarkImageToComponent } from './remark-images-to-component.js';
20
- import remarkPrism from './remark-prism.js';
21
- import remarkShiki from './remark-shiki.js';
22
- import { jsToTreeNode } from './utils.js';
23
-
24
- // Skip nonessential plugins during performance benchmark runs
25
- const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
26
-
27
- export function recmaInjectImportMetaEnvPlugin({
28
- importMetaEnv,
29
- }: {
30
- importMetaEnv: Record<string, any>;
31
- }) {
32
- return (tree: any) => {
33
- estreeVisit(tree, (node) => {
34
- if (node.type === 'MemberExpression') {
35
- // attempt to get "import.meta.env" variable name
36
- const envVarName = getImportMetaEnvVariableName(node as MemberExpression);
37
- if (typeof envVarName === 'string') {
38
- // clear object keys to replace with envVarLiteral
39
- for (const key in node) {
40
- delete (node as any)[key];
41
- }
42
- const envVarLiteral: Literal = {
43
- type: 'Literal',
44
- value: importMetaEnv[envVarName],
45
- raw: JSON.stringify(importMetaEnv[envVarName]),
46
- };
47
- Object.assign(node, envVarLiteral);
48
- }
49
- }
50
- });
51
- };
52
- }
53
-
54
- export function rehypeApplyFrontmatterExport() {
55
- return function (tree: any, vfile: VFile) {
56
- const astroData = safelyGetAstroData(vfile.data);
57
- if (astroData instanceof InvalidAstroDataError)
58
- throw new Error(
59
- // Copied from Astro core `errors-data`
60
- // TODO: find way to import error data from core
61
- '[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`.'
62
- );
63
- const { frontmatter } = astroData;
64
- const exportNodes = [
65
- jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`),
66
- ];
67
- if (frontmatter.layout) {
68
- // NOTE(bholmesdev) 08-22-2022
69
- // Using an async layout import (i.e. `const Layout = (await import...)`)
70
- // Preserves the dev server import cache when globbing a large set of MDX files
71
- // Full explanation: 'https://github.com/withastro/astro/pull/4428'
72
- exportNodes.unshift(
73
- jsToTreeNode(
74
- /** @see 'vite-plugin-markdown' for layout props reference */
75
- `import { jsx as layoutJsx } from 'astro/jsx-runtime';
76
-
77
- export default async function ({ children }) {
78
- const Layout = (await import(${JSON.stringify(frontmatter.layout)})).default;
79
- const { layout, ...content } = frontmatter;
80
- content.file = file;
81
- content.url = url;
82
- return layoutJsx(Layout, {
83
- file,
84
- url,
85
- content,
86
- frontmatter: content,
87
- headings: getHeadings(),
88
- 'server:root': true,
89
- children,
90
- });
91
- };`
92
- )
93
- );
94
- }
95
- tree.children = exportNodes.concat(tree.children);
96
- };
97
- }
98
-
99
- export async function getRemarkPlugins(
100
- mdxOptions: MdxOptions,
101
- config: AstroConfig
102
- ): Promise<MdxRollupPluginOptions['remarkPlugins']> {
103
- let remarkPlugins: PluggableList = [
104
- ...(config.experimental.assets ? [remarkCollectImages, remarkImageToComponent] : []),
105
- ];
106
-
107
- if (!isPerformanceBenchmark) {
108
- if (mdxOptions.gfm) {
109
- remarkPlugins.push(remarkGfm);
110
- }
111
- if (mdxOptions.smartypants) {
112
- remarkPlugins.push(remarkSmartypants);
113
- }
114
- }
115
-
116
- remarkPlugins = [...remarkPlugins, ...mdxOptions.remarkPlugins];
117
-
118
- if (!isPerformanceBenchmark) {
119
- // Apply syntax highlighters after user plugins to match `markdown/remark` behavior
120
- if (mdxOptions.syntaxHighlight === 'shiki') {
121
- remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
122
- }
123
- if (mdxOptions.syntaxHighlight === 'prism') {
124
- remarkPlugins.push(remarkPrism);
125
- }
126
- }
127
-
128
- return remarkPlugins;
129
- }
130
-
131
- export function getRehypePlugins(mdxOptions: MdxOptions): MdxRollupPluginOptions['rehypePlugins'] {
132
- let rehypePlugins: PluggableList = [
133
- // ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters
134
- rehypeMetaString,
135
- // rehypeRaw allows custom syntax highlighters to work without added config
136
- [rehypeRaw, { passThrough: nodeTypes }] as any,
137
- ];
138
-
139
- rehypePlugins = [
140
- ...rehypePlugins,
141
- ...mdxOptions.rehypePlugins,
142
- // getHeadings() is guaranteed by TS, so this must be included.
143
- // We run `rehypeHeadingIds` _last_ to respect any custom IDs set by user plugins.
144
- ...(isPerformanceBenchmark ? [] : [rehypeHeadingIds, rehypeInjectHeadingsExport]),
145
- // computed from `astro.data.frontmatter` in VFile data
146
- rehypeApplyFrontmatterExport,
147
- ];
148
- return rehypePlugins;
149
- }
150
-
151
- /**
152
- * Check if estree entry is "import.meta.env.VARIABLE"
153
- * If it is, return the variable name (i.e. "VARIABLE")
154
- */
155
- function getImportMetaEnvVariableName(node: MemberExpression): string | Error {
156
- try {
157
- // check for ".[ANYTHING]"
158
- if (node.object.type !== 'MemberExpression' || node.property.type !== 'Identifier')
159
- return new Error();
160
-
161
- const nestedExpression = node.object;
162
- // check for ".env"
163
- if (nestedExpression.property.type !== 'Identifier' || nestedExpression.property.name !== 'env')
164
- return new Error();
165
-
166
- const envExpression = nestedExpression.object;
167
- // check for ".meta"
168
- if (
169
- envExpression.type !== 'MetaProperty' ||
170
- envExpression.property.type !== 'Identifier' ||
171
- envExpression.property.name !== 'meta'
172
- )
173
- return new Error();
174
-
175
- // check for "import"
176
- if (envExpression.meta.name !== 'import') return new Error();
177
-
178
- return node.property.name;
179
- } catch (e) {
180
- if (e instanceof Error) {
181
- return e;
182
- }
183
- return new Error('Unknown parsing error');
184
- }
185
- }
@@ -1,11 +0,0 @@
1
- import type { MarkdownHeading, MarkdownVFile } from '@astrojs/markdown-remark';
2
- import { jsToTreeNode } from './utils.js';
3
-
4
- export function rehypeInjectHeadingsExport() {
5
- return function (tree: any, file: MarkdownVFile) {
6
- const headings: MarkdownHeading[] = file.data.__astroHeadings || [];
7
- tree.children.unshift(
8
- jsToTreeNode(`export function getHeadings() { return ${JSON.stringify(headings)} }`)
9
- );
10
- };
11
- }
@@ -1,17 +0,0 @@
1
- import { visit } from 'unist-util-visit';
2
-
3
- /**
4
- * Moves `data.meta` to `properties.metastring` for the `code` element node
5
- * as `rehype-raw` strips `data` from all nodes, which may contain useful information.
6
- * e.g. ```js {1:3} => metastring: "{1:3}"
7
- */
8
- export default function rehypeMetaString() {
9
- return function (tree: any) {
10
- visit(tree, (node) => {
11
- if (node.type === 'element' && node.tagName === 'code' && node.data?.meta) {
12
- node.properties ??= {};
13
- node.properties.metastring = node.data.meta;
14
- }
15
- });
16
- };
17
- }
@@ -1,98 +0,0 @@
1
- import type { MarkdownVFile } from '@astrojs/markdown-remark';
2
- import { type Image, type Parent } from 'mdast';
3
- import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx';
4
- import { visit } from 'unist-util-visit';
5
- import { jsToTreeNode } from './utils.js';
6
-
7
- export function remarkImageToComponent() {
8
- return function (tree: any, file: MarkdownVFile) {
9
- if (!file.data.imagePaths) return;
10
-
11
- const importsStatements: MdxjsEsm[] = [];
12
- const importedImages = new Map<string, string>();
13
-
14
- visit(tree, 'image', (node: Image, index: number | null, parent: Parent | null) => {
15
- // Use the imagePaths set from the remark-collect-images so we don't have to duplicate the logic for
16
- // checking if an image should be imported or not
17
- if (file.data.imagePaths?.has(node.url)) {
18
- let importName = importedImages.get(node.url);
19
-
20
- // If we haven't already imported this image, add an import statement
21
- if (!importName) {
22
- importName = `__${importedImages.size}_${node.url.replace(/\W/g, '_')}__`;
23
-
24
- importsStatements.push({
25
- type: 'mdxjsEsm',
26
- value: '',
27
- data: {
28
- estree: {
29
- type: 'Program',
30
- sourceType: 'module',
31
- body: [
32
- {
33
- type: 'ImportDeclaration',
34
- source: { type: 'Literal', value: node.url, raw: JSON.stringify(node.url) },
35
- specifiers: [
36
- {
37
- type: 'ImportDefaultSpecifier',
38
- local: { type: 'Identifier', name: importName },
39
- },
40
- ],
41
- },
42
- ],
43
- },
44
- },
45
- });
46
- importedImages.set(node.url, importName);
47
- }
48
-
49
- // Build a component that's equivalent to <Image src={importName} alt={node.alt} title={node.title} />
50
- const componentElement: MdxJsxFlowElement = {
51
- name: '__AstroImage__',
52
- type: 'mdxJsxFlowElement',
53
- attributes: [
54
- {
55
- name: 'src',
56
- type: 'mdxJsxAttribute',
57
- value: {
58
- type: 'mdxJsxAttributeValueExpression',
59
- value: importName,
60
- data: {
61
- estree: {
62
- type: 'Program',
63
- sourceType: 'module',
64
- comments: [],
65
- body: [
66
- {
67
- type: 'ExpressionStatement',
68
- expression: { type: 'Identifier', name: importName },
69
- },
70
- ],
71
- },
72
- },
73
- },
74
- },
75
- { name: 'alt', type: 'mdxJsxAttribute', value: node.alt || '' },
76
- ],
77
- children: [],
78
- };
79
-
80
- if (node.title) {
81
- componentElement.attributes.push({
82
- type: 'mdxJsxAttribute',
83
- name: 'title',
84
- value: node.title,
85
- });
86
- }
87
-
88
- parent!.children.splice(index!, 1, componentElement);
89
- }
90
- });
91
-
92
- // Add all the import statements to the top of the file for the images
93
- tree.children.unshift(...importsStatements);
94
-
95
- // Add an import statement for the Astro Image component, we rename it to avoid conflicts
96
- tree.children.unshift(jsToTreeNode(`import { Image as __AstroImage__ } from "astro:assets";`));
97
- };
98
- }
@@ -1,18 +0,0 @@
1
- import { runHighlighterWithAstro } from '@astrojs/prism/dist/highlighter';
2
- import { visit } from 'unist-util-visit';
3
-
4
- /** */
5
- export default function remarkPrism() {
6
- return (tree: any) =>
7
- visit(tree, 'code', (node: any) => {
8
- let { lang, value } = node;
9
- node.type = 'html';
10
-
11
- let { html, classLanguage } = runHighlighterWithAstro(lang, value);
12
- let classes = [classLanguage];
13
- node.value = `<pre class="${classes.join(
14
- ' '
15
- )}"><code class="${classLanguage}">${html}</code></pre>`;
16
- return node;
17
- });
18
- }
@@ -1,115 +0,0 @@
1
- import type { ShikiConfig } from 'astro';
2
- import type * as shiki from 'shiki';
3
- import { getHighlighter } from 'shiki';
4
- import { visit } from 'unist-util-visit';
5
-
6
- /**
7
- * getHighlighter() is the most expensive step of Shiki. Instead of calling it on every page,
8
- * cache it here as much as possible. Make sure that your highlighters can be cached, state-free.
9
- * We make this async, so that multiple calls to parse markdown still share the same highlighter.
10
- */
11
- const highlighterCacheAsync = new Map<string, Promise<shiki.Highlighter>>();
12
-
13
- // Map of old theme names to new names to preserve compatibility when we upgrade shiki
14
- const compatThemes: Record<string, string> = {
15
- 'material-darker': 'material-theme-darker',
16
- 'material-default': 'material-theme',
17
- 'material-lighter': 'material-theme-lighter',
18
- 'material-ocean': 'material-theme-ocean',
19
- 'material-palenight': 'material-theme-palenight',
20
- };
21
-
22
- const normalizeTheme = (theme: string | shiki.IShikiTheme) => {
23
- if (typeof theme === 'string') {
24
- return compatThemes[theme] || theme;
25
- } else if (compatThemes[theme.name]) {
26
- return { ...theme, name: compatThemes[theme.name] };
27
- } else {
28
- return theme;
29
- }
30
- };
31
-
32
- const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: ShikiConfig) => {
33
- theme = normalizeTheme(theme);
34
- const cacheID: string = typeof theme === 'string' ? theme : theme.name;
35
- let highlighterAsync = highlighterCacheAsync.get(cacheID);
36
- if (!highlighterAsync) {
37
- highlighterAsync = getHighlighter({ theme }).then((hl) => {
38
- hl.setColorReplacements({
39
- '#000001': 'var(--astro-code-color-text)',
40
- '#000002': 'var(--astro-code-color-background)',
41
- '#000004': 'var(--astro-code-token-constant)',
42
- '#000005': 'var(--astro-code-token-string)',
43
- '#000006': 'var(--astro-code-token-comment)',
44
- '#000007': 'var(--astro-code-token-keyword)',
45
- '#000008': 'var(--astro-code-token-parameter)',
46
- '#000009': 'var(--astro-code-token-function)',
47
- '#000010': 'var(--astro-code-token-string-expression)',
48
- '#000011': 'var(--astro-code-token-punctuation)',
49
- '#000012': 'var(--astro-code-token-link)',
50
- });
51
- return hl;
52
- });
53
- highlighterCacheAsync.set(cacheID, highlighterAsync);
54
- }
55
- const highlighter = await highlighterAsync;
56
-
57
- // NOTE: There may be a performance issue here for large sites that use `lang`.
58
- // Since this will be called on every page load. Unclear how to fix this.
59
- for (const lang of langs) {
60
- await highlighter.loadLanguage(lang);
61
- }
62
-
63
- return () => (tree: any) => {
64
- visit(tree, 'code', (node) => {
65
- let lang: string;
66
-
67
- if (typeof node.lang === 'string') {
68
- const langExists = highlighter.getLoadedLanguages().includes(node.lang);
69
- if (langExists) {
70
- lang = node.lang;
71
- } else {
72
- // eslint-disable-next-line no-console
73
- console.warn(`The language "${node.lang}" doesn't exist, falling back to plaintext.`);
74
- lang = 'plaintext';
75
- }
76
- } else {
77
- lang = 'plaintext';
78
- }
79
-
80
- let html = highlighter!.codeToHtml(node.value, { lang });
81
-
82
- // Q: Couldn't these regexes match on a user's inputted code blocks?
83
- // A: Nope! All rendered HTML is properly escaped.
84
- // Ex. If a user typed `<span class="line"` into a code block,
85
- // It would become this before hitting our regexes:
86
- // &lt;span class=&quot;line&quot;
87
-
88
- // Replace "shiki" class naming with "astro".
89
- html = html.replace(/<pre class="(.*?)shiki(.*?)"/, `<pre class="$1astro-code$2"`);
90
- // Add "user-select: none;" for "+"/"-" diff symbols
91
- if (node.lang === 'diff') {
92
- html = html.replace(
93
- /<span class="line"><span style="(.*?)">([\+|\-])/g,
94
- '<span class="line"><span style="$1"><span style="user-select: none;">$2</span>'
95
- );
96
- }
97
- // Handle code wrapping
98
- // if wrap=null, do nothing.
99
- if (wrap === false) {
100
- html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto;"');
101
- } else if (wrap === true) {
102
- html = html.replace(
103
- /style="(.*?)"/,
104
- 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"'
105
- );
106
- }
107
-
108
- node.type = 'html';
109
- node.value = html;
110
- node.children = [];
111
- });
112
- };
113
- };
114
-
115
- export default remarkShiki;