diff options
Diffstat (limited to 'packages/integrations/markdoc')
8 files changed, 142 insertions, 32 deletions
diff --git a/packages/integrations/markdoc/README.md b/packages/integrations/markdoc/README.md index 2cc8f32e5..ba107ea75 100644 --- a/packages/integrations/markdoc/README.md +++ b/packages/integrations/markdoc/README.md @@ -237,6 +237,20 @@ const { Content } = await entry.render(); /> ``` +### Access frontmatter and content collection information from your templates + +You can access content collection information from your Markdoc templates using the `$entry` variable. This includes the entry `slug`, `collection` name, and frontmatter `data` parsed by your content collection schema (if any). This example renders the `title` frontmatter property as a heading: + +```md +--- +title: Welcome to Markdoc 👋 +--- + +# {% $entry.data.title %} +``` + +The `$entry` object matches [the `CollectionEntry` type](https://docs.astro.build/en/reference/api-reference/#collection-entry-type), excluding the `.render()` property. + ### Markdoc config The Markdoc integration accepts [all Markdoc configuration options](https://markdoc.dev/docs/config), including [tags](https://markdoc.dev/docs/tags) and [functions](https://markdoc.dev/docs/functions). diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts index 71e117de4..70d005ee5 100644 --- a/packages/integrations/markdoc/src/index.ts +++ b/packages/integrations/markdoc/src/index.ts @@ -3,13 +3,7 @@ import Markdoc from '@markdoc/markdoc'; import type { AstroConfig, AstroIntegration, ContentEntryType, HookParameters } from 'astro'; import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; -import type { InlineConfig } from 'vite'; -import { - getAstroConfigPath, - MarkdocError, - parseFrontmatter, - prependForwardSlash, -} from './utils.js'; +import { getAstroConfigPath, MarkdocError, parseFrontmatter } from './utils.js'; type SetupHookParams = HookParameters<'astro:config:setup'> & { // `contentEntryType` is not a public API @@ -36,36 +30,27 @@ export default function markdoc(markdocConfig: Config = {}): AstroIntegration { addContentEntryType({ extensions: ['.mdoc'], getEntryInfo, + getRenderModule({ entry }) { + validateRenderProperties(markdocConfig, config); + const ast = Markdoc.parse(entry.body); + const content = Markdoc.transform(ast, { + ...markdocConfig, + variables: { + ...markdocConfig.variables, + entry, + }, + }); + return { + code: `import { jsx as h } from 'astro/jsx-runtime';\nimport { Renderer } from '@astrojs/markdoc/components';\nconst transformedContent = ${JSON.stringify( + content + )};\nexport async function Content ({ components }) { return h(Renderer, { content: transformedContent, components }); }\nContent[Symbol.for('astro.needsHeadRendering')] = true;`, + }; + }, contentModuleTypes: await fs.promises.readFile( new URL('../template/content-module-types.d.ts', import.meta.url), 'utf-8' ), }); - - const viteConfig: InlineConfig = { - plugins: [ - { - name: '@astrojs/markdoc', - async transform(code, id) { - if (!id.endsWith('.mdoc')) return; - - validateRenderProperties(markdocConfig, config); - const body = getEntryInfo({ - // Can't use `pathToFileUrl` - Vite IDs are not plain file paths - fileUrl: new URL(prependForwardSlash(id), 'file://'), - contents: code, - }).body; - const ast = Markdoc.parse(body); - const content = Markdoc.transform(ast, markdocConfig); - - return `import { jsx as h } from 'astro/jsx-runtime';\nimport { Renderer } from '@astrojs/markdoc/components';\nconst transformedContent = ${JSON.stringify( - content - )};\nexport async function Content ({ components }) { return h(Renderer, { content: transformedContent, components }); }\nContent[Symbol.for('astro.needsHeadRendering')] = true;`; - }, - }, - ], - }; - updateConfig({ vite: viteConfig }); }, }, }; diff --git a/packages/integrations/markdoc/test/entry-prop.test.js b/packages/integrations/markdoc/test/entry-prop.test.js new file mode 100644 index 000000000..b47ccf739 --- /dev/null +++ b/packages/integrations/markdoc/test/entry-prop.test.js @@ -0,0 +1,58 @@ +import { parseHTML } from 'linkedom'; +import { expect } from 'chai'; +import { loadFixture } from '../../../astro/test/test-utils.js'; +import markdoc from '../dist/index.js'; + +const root = new URL('./fixtures/entry-prop/', import.meta.url); + +describe('Markdoc - Entry prop', () => { + let baseFixture; + + before(async () => { + baseFixture = await loadFixture({ + root, + integrations: [markdoc()], + }); + }); + + describe('dev', () => { + let devServer; + + before(async () => { + devServer = await baseFixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('has expected entry properties', async () => { + const res = await baseFixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + expect(document.querySelector('h1')?.textContent).to.equal('Processed by schema: Test entry'); + expect(document.getElementById('id')?.textContent?.trim()).to.equal('id: entry.mdoc'); + expect(document.getElementById('slug')?.textContent?.trim()).to.equal('slug: entry'); + expect(document.getElementById('collection')?.textContent?.trim()).to.equal( + 'collection: blog' + ); + }); + }); + + describe('build', () => { + before(async () => { + await baseFixture.build(); + }); + + it('has expected entry properties', async () => { + const html = await baseFixture.readFile('/index.html'); + const { document } = parseHTML(html); + expect(document.querySelector('h1')?.textContent).to.equal('Processed by schema: Test entry'); + expect(document.getElementById('id')?.textContent?.trim()).to.equal('id: entry.mdoc'); + expect(document.getElementById('slug')?.textContent?.trim()).to.equal('slug: entry'); + expect(document.getElementById('collection')?.textContent?.trim()).to.equal( + 'collection: blog' + ); + }); + }); +}); diff --git a/packages/integrations/markdoc/test/fixtures/entry-prop/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/entry-prop/astro.config.mjs new file mode 100644 index 000000000..29d846359 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/entry-prop/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import markdoc from '@astrojs/markdoc'; + +// https://astro.build/config +export default defineConfig({ + integrations: [markdoc()], +}); diff --git a/packages/integrations/markdoc/test/fixtures/entry-prop/package.json b/packages/integrations/markdoc/test/fixtures/entry-prop/package.json new file mode 100644 index 000000000..149f6c35a --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/entry-prop/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/markdoc-entry-prop", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/markdoc": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/blog/entry.mdoc b/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/blog/entry.mdoc new file mode 100644 index 000000000..151d5a81d --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/blog/entry.mdoc @@ -0,0 +1,9 @@ +--- +title: Test entry +--- + +# {% $entry.data.title %} + +- id: {% $entry.id %} {% #id %} +- slug: {% $entry.slug %} {% #slug %} +- collection: {% $entry.collection %} {% #collection %} diff --git a/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/config.ts b/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/config.ts new file mode 100644 index 000000000..ff473d4af --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/entry-prop/src/content/config.ts @@ -0,0 +1,9 @@ +import { defineCollection, z } from 'astro:content'; + +const blog = defineCollection({ + schema: z.object({ + title: z.string().transform(v => 'Processed by schema: ' + v), + }), +}); + +export const collections = { blog } diff --git a/packages/integrations/markdoc/test/fixtures/entry-prop/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/entry-prop/src/pages/index.astro new file mode 100644 index 000000000..d14187651 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/entry-prop/src/pages/index.astro @@ -0,0 +1,19 @@ +--- +import { getEntryBySlug } from 'astro:content'; + +const entry = await getEntryBySlug('blog', 'entry'); +const { Content } = await entry.render(); +--- + +<html lang="en"> + <head> + <meta charset="utf-8" /> + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> + <meta name="viewport" content="width=device-width" /> + <meta name="generator" content={Astro.generator} /> + <title>Astro</title> + </head> + <body> + <Content /> + </body> +</html> |