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: