diff options
Diffstat (limited to 'packages/integrations/mdx')
8 files changed, 39 insertions, 83 deletions
diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index d788a171a..e9d81ca1f 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -1,3 +1,4 @@ +import { toRemarkInitializeAstroData } from '@astrojs/markdown-remark/dist/internal.js'; import { compile as mdxCompile } from '@mdx-js/mdx'; import { PluggableList } from '@mdx-js/mdx/lib/core.js'; import mdxPlugin, { Options as MdxRollupPluginOptions } from '@mdx-js/rollup'; @@ -7,12 +8,7 @@ import fs from 'node:fs/promises'; import type { Options as RemarkRehypeOptions } from 'remark-rehype'; import { VFile } from 'vfile'; import type { Plugin as VitePlugin } from 'vite'; -import { - getRehypePlugins, - getRemarkPlugins, - recmaInjectImportMetaEnvPlugin, - rehypeApplyFrontmatterExport, -} from './plugins.js'; +import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from './plugins.js'; import { getFileInfo, parseFrontmatter } from './utils.js'; const RAW_CONTENT_ERROR = @@ -86,9 +82,10 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration { const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), { ...mdxPluginOpts, - rehypePlugins: [ - ...(mdxPluginOpts.rehypePlugins ?? []), - () => rehypeApplyFrontmatterExport(frontmatter), + remarkPlugins: [ + // Ensure `data.astro` is available to all remark plugins + toRemarkInitializeAstroData({ userFrontmatter: frontmatter }), + ...(mdxPluginOpts.remarkPlugins ?? []), ], recmaPlugins: [ ...(mdxPluginOpts.recmaPlugins ?? []), diff --git a/packages/integrations/mdx/src/plugins.ts b/packages/integrations/mdx/src/plugins.ts index c823c6e7c..4701b1679 100644 --- a/packages/integrations/mdx/src/plugins.ts +++ b/packages/integrations/mdx/src/plugins.ts @@ -2,7 +2,11 @@ import { rehypeHeadingIds } from '@astrojs/markdown-remark'; import { nodeTypes } from '@mdx-js/mdx'; import type { PluggableList } from '@mdx-js/mdx/lib/core.js'; import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup'; -import type { AstroConfig, MarkdownAstroData } from 'astro'; +import type { AstroConfig } from 'astro'; +import { + safelyGetAstroData, + InvalidAstroDataError, +} from '@astrojs/markdown-remark/dist/internal.js'; import type { Literal, MemberExpression } from 'estree'; import { visit as estreeVisit } from 'estree-util-visit'; import { bold, yellow } from 'kleur/colors'; @@ -47,26 +51,18 @@ export function recmaInjectImportMetaEnvPlugin({ }; } -export function remarkInitializeAstroData() { +export function rehypeApplyFrontmatterExport() { return function (tree: any, vfile: VFile) { - if (!vfile.data.astro) { - vfile.data.astro = { frontmatter: {} }; - } - }; -} - -export function rehypeApplyFrontmatterExport(pageFrontmatter: Record<string, any>) { - return function (tree: any, vfile: VFile) { - const { frontmatter: injectedFrontmatter } = safelyGetAstroData(vfile.data); - const frontmatter = { ...injectedFrontmatter, ...pageFrontmatter }; + const astroData = safelyGetAstroData(vfile.data); + if (astroData instanceof InvalidAstroDataError) + throw new Error( + // Copied from Astro core `errors-data` + // TODO: find way to import error data from core + '[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`.' + ); + const { frontmatter } = astroData; const exportNodes = [ - jsToTreeNode( - `export const frontmatter = ${JSON.stringify( - frontmatter - )};\nexport const _internal = { injectedFrontmatter: ${JSON.stringify( - injectedFrontmatter - )} };` - ), + jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`), ]; if (frontmatter.layout) { // NOTE(bholmesdev) 08-22-2022 @@ -151,10 +147,7 @@ export async function getRemarkPlugins( mdxOptions: MdxOptions, config: AstroConfig ): Promise<MdxRollupPluginOptions['remarkPlugins']> { - let remarkPlugins: PluggableList = [ - // Set "vfile.data.astro" for plugins to inject frontmatter - remarkInitializeAstroData, - ]; + let remarkPlugins: PluggableList = []; switch (mdxOptions.extendPlugins) { case false: break; @@ -217,6 +210,8 @@ export function getRehypePlugins( // We run `rehypeHeadingIds` _last_ to respect any custom IDs set by user plugins. rehypeHeadingIds, rehypeInjectHeadingsExport, + // computed from `astro.data.frontmatter` in VFile data + rehypeApplyFrontmatterExport, ]; return rehypePlugins; } @@ -251,41 +246,6 @@ function ignoreStringPlugins(plugins: any[]) { } /** - * Copied from markdown utils - * @see "vite-plugin-utils" - */ -function isValidAstroData(obj: unknown): obj is MarkdownAstroData { - if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty('frontmatter')) { - const { frontmatter } = obj as any; - try { - // ensure frontmatter is JSON-serializable - JSON.stringify(frontmatter); - } catch { - return false; - } - return typeof frontmatter === 'object' && frontmatter !== null; - } - return false; -} - -/** - * Copied from markdown utils - * @see "vite-plugin-utils" - */ -function safelyGetAstroData(vfileData: Data): MarkdownAstroData { - const { astro } = vfileData; - - if (!astro) return { frontmatter: {} }; - if (!isValidAstroData(astro)) { - throw Error( - `[MDX] A remark or rehype plugin tried to add invalid frontmatter. Ensure "astro.frontmatter" is a JSON object!` - ); - } - - return astro; -} - -/** * Check if estree entry is "import.meta.env.VARIABLE" * If it is, return the variable name (i.e. "VARIABLE") */ diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/astro.config.mjs b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/astro.config.mjs index fc15686c2..5335fac87 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/astro.config.mjs +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/astro.config.mjs @@ -1,12 +1,12 @@ import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; -import { rehypeReadingTime, remarkTitle } from './src/markdown-plugins.mjs'; +import { rehypeReadingTime, remarkDescription, remarkTitle } from './src/markdown-plugins.mjs'; // https://astro.build/config export default defineConfig({ site: 'https://astro.build/', integrations: [mdx({ - remarkPlugins: [remarkTitle], + remarkPlugins: [remarkTitle, remarkDescription], rehypePlugins: [rehypeReadingTime], })], }); diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs index c0d5f7b2e..35b415787 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/markdown-plugins.mjs @@ -18,3 +18,10 @@ export function remarkTitle() { }); }; } + +export function remarkDescription() { + return function (tree, vfile) { + const { frontmatter } = vfile.data.astro; + frontmatter.description = `Processed by remarkDescription plugin: ${frontmatter.description}` + }; +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx index 1092099f0..0d96d95b9 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx @@ -1,5 +1,6 @@ --- layout: '../layouts/Base.astro' +description: Page 1 description --- # Page 1 diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx index c82eb97c2..fe6a8286b 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx @@ -1,5 +1,6 @@ --- layout: '../layouts/Base.astro' +description: Page 2 description --- # Page 2 diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx deleted file mode 100644 index 4e11c1c37..000000000 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/with-overrides.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: 'Overridden title' -injectedReadingTime: - text: '1000 min read' ---- - -# Working! diff --git a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js index 780f7252c..8f598b78e 100644 --- a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js +++ b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js @@ -33,14 +33,11 @@ describe('MDX frontmatter injection', () => { } }); - it('overrides injected frontmatter with user frontmatter', async () => { + it('allow user frontmatter mutation', async () => { const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json')); - const readingTimes = frontmatterByPage.map( - (frontmatter = {}) => frontmatter.injectedReadingTime?.text - ); - const titles = frontmatterByPage.map((frontmatter = {}) => frontmatter.title); - expect(titles).to.contain('Overridden title'); - expect(readingTimes).to.contain('1000 min read'); + const descriptions = frontmatterByPage.map((frontmatter = {}) => frontmatter.description); + expect(descriptions).to.contain('Processed by remarkDescription plugin: Page 1 description'); + expect(descriptions).to.contain('Processed by remarkDescription plugin: Page 2 description'); }); it('passes injected frontmatter to layouts', async () => { |