summaryrefslogtreecommitdiff
path: root/packages/integrations/markdoc
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/markdoc')
-rw-r--r--packages/integrations/markdoc/README.md14
-rw-r--r--packages/integrations/markdoc/src/index.ts49
-rw-r--r--packages/integrations/markdoc/test/entry-prop.test.js58
-rw-r--r--packages/integrations/markdoc/test/fixtures/entry-prop/astro.config.mjs7
-rw-r--r--packages/integrations/markdoc/test/fixtures/entry-prop/package.json9
-rw-r--r--packages/integrations/markdoc/test/fixtures/entry-prop/src/content/blog/entry.mdoc9
-rw-r--r--packages/integrations/markdoc/test/fixtures/entry-prop/src/content/config.ts9
-rw-r--r--packages/integrations/markdoc/test/fixtures/entry-prop/src/pages/index.astro19
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>