diff --git a/.changeset/mean-guests-joke.md b/.changeset/mean-guests-joke.md
new file mode 100644
index 000000000..c55693e50
--- /dev/null
+++ b/.changeset/mean-guests-joke.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/markdoc': patch
+---
+
+Fix: when using `render: null` in your config, content is now rendered without a wrapper element.
diff --git a/packages/integrations/markdoc/components/TreeNode.ts b/packages/integrations/markdoc/components/TreeNode.ts
index 8a3158589..a60597a0d 100644
--- a/packages/integrations/markdoc/components/TreeNode.ts
+++ b/packages/integrations/markdoc/components/TreeNode.ts
@@ -1,4 +1,5 @@
import type { AstroInstance } from 'astro';
+import { Fragment } from 'astro/jsx-runtime';
import type { RenderableTreeNode } from '@markdoc/markdoc';
import Markdoc from '@markdoc/markdoc';
import { createComponent, renderComponent, render } from 'astro/runtime/server/index.js';
@@ -44,9 +45,16 @@ export const ComponentNode = createComponent({
propagation: 'none',
});
-export function createTreeNode(node: RenderableTreeNode): TreeNode {
+export function createTreeNode(node: RenderableTreeNode | RenderableTreeNode[]): TreeNode {
if (typeof node === 'string' || typeof node === 'number') {
return { type: 'text', content: String(node) };
+ } else if (Array.isArray(node)) {
+ return {
+ type: 'component',
+ component: Fragment,
+ props: {},
+ children: node.map((child) => createTreeNode(child)),
+ };
} else if (node === null || typeof node !== 'object' || !Markdoc.Tag.isTag(node)) {
return { type: 'text', content: '' };
}
diff --git a/packages/integrations/markdoc/test/fixtures/render-null/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/render-null/astro.config.mjs
new file mode 100644
index 000000000..29d846359
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-null/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/render-null/markdoc.config.mjs b/packages/integrations/markdoc/test/fixtures/render-null/markdoc.config.mjs
new file mode 100644
index 000000000..2f87f6de0
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-null/markdoc.config.mjs
@@ -0,0 +1,26 @@
+import { defineMarkdocConfig } from '@astrojs/markdoc/config';
+
+export default defineMarkdocConfig({
+ nodes: {
+ document: {
+ render: null,
+
+ // Defaults from `Markdoc.nodes.document`
+ children: [
+ 'heading',
+ 'paragraph',
+ 'image',
+ 'table',
+ 'tag',
+ 'fence',
+ 'blockquote',
+ 'comment',
+ 'list',
+ 'hr',
+ ],
+ attributes: {
+ frontmatter: { render: false },
+ },
+ }
+ }
+})
diff --git a/packages/integrations/markdoc/test/fixtures/render-null/package.json b/packages/integrations/markdoc/test/fixtures/render-null/package.json
new file mode 100644
index 000000000..e9529b693
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-null/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/markdoc-render-null",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/markdoc": "workspace:*",
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/integrations/markdoc/test/fixtures/render-null/src/content/blog/render-null.mdoc b/packages/integrations/markdoc/test/fixtures/render-null/src/content/blog/render-null.mdoc
new file mode 100644
index 000000000..7b7b193cb
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-null/src/content/blog/render-null.mdoc
@@ -0,0 +1,7 @@
+---
+title: Post with render null
+---
+
+## Post with render null
+
+This should render the contents inside a fragment!
diff --git a/packages/integrations/markdoc/test/fixtures/render-null/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/render-null/src/pages/index.astro
new file mode 100644
index 000000000..ed8417c5b
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-null/src/pages/index.astro
@@ -0,0 +1,19 @@
+---
+import { getEntryBySlug } from "astro:content";
+
+const post = await getEntryBySlug('blog', 'render-null');
+const { Content } = await post.render();
+---
+
+
+
+
+
+
+
+ Content
+
+
+
+
+
diff --git a/packages/integrations/markdoc/test/render.test.js b/packages/integrations/markdoc/test/render.test.js
index acb17577b..48d13a759 100644
--- a/packages/integrations/markdoc/test/render.test.js
+++ b/packages/integrations/markdoc/test/render.test.js
@@ -16,11 +16,8 @@ describe('Markdoc - render', () => {
const res = await fixture.fetch('/');
const html = await res.text();
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Simple post');
- const p = document.querySelector('p');
- expect(p.textContent).to.equal('This is a simple Markdoc post.');
+
+ renderSimpleChecks(html);
await server.stop();
});
@@ -31,17 +28,8 @@ describe('Markdoc - render', () => {
const res = await fixture.fetch('/');
const html = await res.text();
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Post with config');
- const textContent = html;
- expect(textContent).to.not.include('Hello');
- expect(textContent).to.include('Hola');
- expect(textContent).to.include(`Konnichiwa`);
-
- const runtimeVariable = document.querySelector('#runtime-variable');
- expect(runtimeVariable?.textContent?.trim()).to.equal('working!');
+ renderConfigChecks(html);
await server.stop();
});
@@ -52,19 +40,20 @@ describe('Markdoc - render', () => {
const res = await fixture.fetch('/');
const html = await res.text();
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Post with components');
- // Renders custom shortcode component
- const marquee = document.querySelector('marquee');
- expect(marquee).to.not.be.null;
- expect(marquee.hasAttribute('data-custom-marquee')).to.equal(true);
+ renderComponentsChecks(html);
- // Renders Astro Code component
- const pre = document.querySelector('pre');
- expect(pre).to.not.be.null;
- expect(pre.className).to.equal('astro-code');
+ await server.stop();
+ });
+
+ it('renders content - with `render: null` in document', async () => {
+ const fixture = await getFixture('render-null');
+ const server = await fixture.startDevServer();
+
+ const res = await fixture.fetch('/');
+ const html = await res.text();
+
+ renderNullChecks(html);
await server.stop();
});
@@ -76,11 +65,8 @@ describe('Markdoc - render', () => {
await fixture.build();
const html = await fixture.readFile('/index.html');
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Simple post');
- const p = document.querySelector('p');
- expect(p.textContent).to.equal('This is a simple Markdoc post.');
+
+ renderSimpleChecks(html);
});
it('renders content - with config', async () => {
@@ -88,17 +74,8 @@ describe('Markdoc - render', () => {
await fixture.build();
const html = await fixture.readFile('/index.html');
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Post with config');
- const textContent = html;
- expect(textContent).to.not.include('Hello');
- expect(textContent).to.include('Hola');
- expect(textContent).to.include(`Konnichiwa`);
-
- const runtimeVariable = document.querySelector('#runtime-variable');
- expect(runtimeVariable?.textContent?.trim()).to.equal('working!');
+ renderConfigChecks(html);
});
it('renders content - with components', async () => {
@@ -106,19 +83,68 @@ describe('Markdoc - render', () => {
await fixture.build();
const html = await fixture.readFile('/index.html');
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- expect(h2.textContent).to.equal('Post with components');
- // Renders custom shortcode component
- const marquee = document.querySelector('marquee');
- expect(marquee).to.not.be.null;
- expect(marquee.hasAttribute('data-custom-marquee')).to.equal(true);
+ renderComponentsChecks(html);
+ });
- // Renders Astro Code component
- const pre = document.querySelector('pre');
- expect(pre).to.not.be.null;
- expect(pre.className).to.equal('astro-code');
+ it('renders content - with `render: null` in document', async () => {
+ const fixture = await getFixture('render-null');
+ await fixture.build();
+
+ const html = await fixture.readFile('/index.html');
+
+ renderNullChecks(html);
});
});
});
+
+/**
+ * @param {string} html
+ */
+function renderNullChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ expect(h2.textContent).to.equal('Post with render null');
+ expect(h2.parentElement?.tagName).to.equal('BODY');
+}
+
+/** @param {string} html */
+function renderComponentsChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ expect(h2.textContent).to.equal('Post with components');
+
+ // Renders custom shortcode component
+ const marquee = document.querySelector('marquee');
+ expect(marquee).to.not.be.null;
+ expect(marquee.hasAttribute('data-custom-marquee')).to.equal(true);
+
+ // Renders Astro Code component
+ const pre = document.querySelector('pre');
+ expect(pre).to.not.be.null;
+ expect(pre.className).to.equal('astro-code');
+}
+
+/** @param {string} html */
+function renderConfigChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ expect(h2.textContent).to.equal('Post with config');
+ const textContent = html;
+
+ expect(textContent).to.not.include('Hello');
+ expect(textContent).to.include('Hola');
+ expect(textContent).to.include(`Konnichiwa`);
+
+ const runtimeVariable = document.querySelector('#runtime-variable');
+ expect(runtimeVariable?.textContent?.trim()).to.equal('working!');
+}
+
+/** @param {string} html */
+function renderSimpleChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ expect(h2.textContent).to.equal('Simple post');
+ const p = document.querySelector('p');
+ expect(p.textContent).to.equal('This is a simple Markdoc post.');
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4f644045c..79cb4699f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -332,8 +332,8 @@ importers:
astro: ^2.1.9
kleur: ^4.1.5
dependencies:
- '@astrojs/markdoc': link:../../packages/integrations/markdoc
- astro: link:../../packages/astro
+ '@astrojs/markdoc': 0.1.0_astro@2.1.9
+ astro: 2.1.9
kleur: 4.1.5
examples/with-markdown-plugins:
@@ -3150,6 +3150,14 @@ importers:
'@astrojs/markdoc': link:../../..
astro: link:../../../../../astro
+ packages/integrations/markdoc/test/fixtures/render-null:
+ specifiers:
+ '@astrojs/markdoc': workspace:*
+ astro: workspace:*
+ dependencies:
+ '@astrojs/markdoc': link:../../..
+ astro: link:../../../../../astro
+
packages/integrations/markdoc/test/fixtures/render-simple:
specifiers:
'@astrojs/markdoc': workspace:*
@@ -4268,6 +4276,46 @@ packages:
- react
dev: false
+ /@astrojs/markdoc/0.1.0_astro@2.1.9:
+ resolution: {integrity: sha512-t+9pDDi8JpAoUfkHI7V8lGxrtbYx4nx3QZ5OOdbMtj5BTUqyR+rVQyA5dRcIsEFvg2Wfqb/BqsjpXOrt75s4UA==}
+ engines: {node: '>=16.12.0'}
+ peerDependencies:
+ astro: '*'
+ dependencies:
+ '@markdoc/markdoc': 0.2.2
+ astro: 2.1.9
+ esbuild: 0.17.12
+ gray-matter: 4.0.3
+ kleur: 4.1.5
+ zod: 3.20.6
+ transitivePeerDependencies:
+ - '@types/react'
+ - react
+ dev: false
+
+ /@astrojs/markdown-remark/2.1.2_astro@2.1.9:
+ resolution: {integrity: sha512-rYkmFEv2w7oEk6ZPgxHkhWzwcxSUGc1vJU0cbCu5sHF8iFNnc1cmMsjXWa5DrU5sCEf8VVYE1iFlbbnFzvHQJw==}
+ peerDependencies:
+ astro: '*'
+ dependencies:
+ '@astrojs/prism': 2.1.1
+ astro: 2.1.9
+ github-slugger: 1.5.0
+ import-meta-resolve: 2.2.1
+ rehype-raw: 6.1.1
+ rehype-stringify: 9.0.3
+ remark-gfm: 3.0.1
+ remark-parse: 10.0.1
+ remark-rehype: 10.1.0
+ remark-smartypants: 2.0.0
+ shiki: 0.11.1
+ unified: 10.1.2
+ unist-util-visit: 4.1.2
+ vfile: 5.3.7
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/@astrojs/markdown-remark/2.1.2_astro@packages+astro:
resolution: {integrity: sha512-rYkmFEv2w7oEk6ZPgxHkhWzwcxSUGc1vJU0cbCu5sHF8iFNnc1cmMsjXWa5DrU5sCEf8VVYE1iFlbbnFzvHQJw==}
peerDependencies:
@@ -4347,6 +4395,22 @@ packages:
prismjs: 1.29.0
dev: false
+ /@astrojs/telemetry/2.1.0:
+ resolution: {integrity: sha512-P3gXNNOkRJM8zpnasNoi5kXp3LnFt0smlOSUXhkynfJpTJMIDrcMbKpNORN0OYbqpKt9JPdgRN7nsnGWpbH1ww==}
+ engines: {node: '>=16.12.0'}
+ dependencies:
+ ci-info: 3.7.1
+ debug: 4.3.4
+ dlv: 1.1.3
+ dset: 3.1.2
+ is-docker: 3.0.0
+ is-wsl: 2.2.0
+ undici: 5.20.0
+ which-pm-runs: 1.1.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/@astrojs/webapi/1.1.1:
resolution: {integrity: sha512-yeUvP27PoiBK/WCxyQzC4HLYZo4Hg6dzRd/dTsL50WGlAQVCwWcqzVJrIZKvzNDNaW/fIXutZTmdj6nec0PIGg==}
dependencies:
@@ -4354,6 +4418,12 @@ packages:
node-fetch: 3.3.0
dev: false
+ /@astrojs/webapi/2.1.0:
+ resolution: {integrity: sha512-sbF44s/uU33jAdefzKzXZaENPeXR0sR3ptLs+1xp9xf5zIBhedH2AfaFB5qTEv9q5udUVoKxubZGT3G1nWs6rA==}
+ dependencies:
+ undici: 5.20.0
+ dev: false
+
/@babel/code-frame/7.18.6:
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
engines: {node: '>=6.9.0'}
@@ -8790,6 +8860,79 @@ packages:
ultrahtml: 0.1.3
dev: false
+ /astro/2.1.9:
+ resolution: {integrity: sha512-UkbG0lgue1b/t4yMI+AkAGEfdOwcPS2RUYQ/QIurtKjP6W5gtKQveRTBuHH7iwiBziH+z8Ecc5/OAALoXSvMlQ==}
+ engines: {node: '>=16.12.0', npm: '>=6.14.0'}
+ hasBin: true
+ peerDependencies:
+ sharp: ^0.31.3
+ peerDependenciesMeta:
+ sharp:
+ optional: true
+ dependencies:
+ '@astrojs/compiler': 1.3.0
+ '@astrojs/language-server': 0.28.3
+ '@astrojs/markdown-remark': 2.1.2_astro@2.1.9
+ '@astrojs/telemetry': 2.1.0
+ '@astrojs/webapi': 2.1.0
+ '@babel/core': 7.20.12
+ '@babel/generator': 7.20.14
+ '@babel/parser': 7.20.15
+ '@babel/plugin-transform-react-jsx': 7.20.13_@babel+core@7.20.12
+ '@babel/traverse': 7.20.13
+ '@babel/types': 7.20.7
+ '@types/babel__core': 7.20.0
+ '@types/yargs-parser': 21.0.0
+ acorn: 8.8.2
+ boxen: 6.2.1
+ chokidar: 3.5.3
+ ci-info: 3.7.1
+ common-ancestor-path: 1.0.1
+ cookie: 0.5.0
+ debug: 4.3.4
+ deepmerge-ts: 4.3.0
+ devalue: 4.2.3
+ diff: 5.1.0
+ es-module-lexer: 1.1.1
+ estree-walker: 3.0.3
+ execa: 6.1.0
+ fast-glob: 3.2.12
+ github-slugger: 2.0.0
+ gray-matter: 4.0.3
+ html-escaper: 3.0.3
+ kleur: 4.1.5
+ magic-string: 0.27.0
+ mime: 3.0.0
+ ora: 6.1.2
+ path-to-regexp: 6.2.1
+ preferred-pm: 3.0.3
+ prompts: 2.4.2
+ rehype: 12.0.1
+ semver: 7.3.8
+ server-destroy: 1.0.1
+ shiki: 0.11.1
+ slash: 4.0.0
+ string-width: 5.1.2
+ strip-ansi: 7.0.1
+ supports-esm: 1.0.0
+ tsconfig-resolver: 3.0.1
+ typescript: 5.0.2
+ unist-util-visit: 4.1.2
+ vfile: 5.3.7
+ vite: 4.1.2
+ vitefu: 0.2.4_vite@4.1.2
+ yargs-parser: 21.1.1
+ zod: 3.20.6
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: false
+
/async-sema/3.1.1:
resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==}
dev: false
@@ -14986,7 +15129,6 @@ packages:
hasBin: true
optionalDependencies:
fsevents: 2.3.2
- dev: true
/run-parallel/1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -16525,7 +16667,7 @@ packages:
esbuild: 0.16.17
postcss: 8.4.21
resolve: 1.22.1
- rollup: 3.14.0
+ rollup: 3.20.1
optionalDependencies:
fsevents: 2.3.2
@@ -16614,7 +16756,7 @@ packages:
vite:
optional: true
dependencies:
- vite: 4.1.2_sass@1.58.0
+ vite: 4.1.2
dev: false
/vitest/0.20.3: