summaryrefslogtreecommitdiff
path: root/packages/integrations/markdoc
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/markdoc')
-rw-r--r--packages/integrations/markdoc/package.json2
-rw-r--r--packages/integrations/markdoc/src/content-entry-type.ts202
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/_partial.mdoc5
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/with-partial.mdoc5
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/astro.config.mjs7
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/markdoc.config.ts7
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/package.json9
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/_partial.mdoc3
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/with-partials.mdoc7
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/src/content/nested/_partial.mdoc1
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-partials/src/pages/index.astro19
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs3
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.ts10
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/package.json4
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/components/Counter.tsx10
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/components/CounterWrapper.astro5
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/components/DeeplyNested.astro5
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/content/_nested.mdoc3
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/_counter.mdoc7
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/with-components.mdoc2
-rw-r--r--packages/integrations/markdoc/test/fixtures/render-with-components/tsconfig.json7
-rw-r--r--packages/integrations/markdoc/test/render-components.test.js89
-rw-r--r--packages/integrations/markdoc/test/render-html.test.js23
-rw-r--r--packages/integrations/markdoc/test/render-indented-components.test.js67
-rw-r--r--packages/integrations/markdoc/test/render.test.js85
25 files changed, 478 insertions, 109 deletions
diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json
index f1ba10559..ba6c7b360 100644
--- a/packages/integrations/markdoc/package.json
+++ b/packages/integrations/markdoc/package.json
@@ -59,7 +59,7 @@
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\"",
- "test": "astro-scripts test --timeout 40000 \"test/**/*.test.js\""
+ "test": "astro-scripts test --timeout 60000 \"test/**/*.test.js\""
},
"dependencies": {
"@astrojs/internal-helpers": "workspace:*",
diff --git a/packages/integrations/markdoc/src/content-entry-type.ts b/packages/integrations/markdoc/src/content-entry-type.ts
index 89f9f9e86..5168c49c1 100644
--- a/packages/integrations/markdoc/src/content-entry-type.ts
+++ b/packages/integrations/markdoc/src/content-entry-type.ts
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
-import { fileURLToPath } from 'node:url';
+import { fileURLToPath, pathToFileURL } from 'node:url';
import type { Config as MarkdocConfig, Node } from '@markdoc/markdoc';
import Markdoc from '@markdoc/markdoc';
import type { AstroConfig, ContentEntryType } from 'astro';
@@ -38,9 +38,41 @@ export async function getContentEntryType({
}
const ast = Markdoc.parse(tokens);
- const usedTags = getUsedTags(ast);
const userMarkdocConfig = markdocConfigResult?.config ?? {};
const markdocConfigUrl = markdocConfigResult?.fileUrl;
+ const pluginContext = this;
+ const markdocConfig = await setupConfig(userMarkdocConfig, options);
+ const filePath = fileURLToPath(fileUrl);
+ raiseValidationErrors({
+ ast,
+ /* Raised generics issue with Markdoc core https://github.com/markdoc/markdoc/discussions/400 */
+ markdocConfig: markdocConfig as MarkdocConfig,
+ entry,
+ viteId,
+ astroConfig,
+ filePath,
+ });
+ await resolvePartials({
+ ast,
+ markdocConfig: markdocConfig as MarkdocConfig,
+ fileUrl,
+ allowHTML: options?.allowHTML,
+ tokenizer,
+ pluginContext,
+ root: astroConfig.root,
+ raisePartialValidationErrors: (partialAst, partialPath) => {
+ raiseValidationErrors({
+ ast: partialAst,
+ markdocConfig: markdocConfig as MarkdocConfig,
+ entry,
+ viteId,
+ astroConfig,
+ filePath: partialPath,
+ });
+ },
+ });
+
+ const usedTags = getUsedTags(ast);
let componentConfigByTagMap: Record<string, ComponentConfig> = {};
// Only include component imports for tags used in the document.
@@ -59,42 +91,6 @@ export async function getContentEntryType({
}
}
- const pluginContext = this;
- const markdocConfig = await setupConfig(userMarkdocConfig, options);
-
- const filePath = fileURLToPath(fileUrl);
-
- const validationErrors = Markdoc.validate(
- ast,
- /* Raised generics issue with Markdoc core https://github.com/markdoc/markdoc/discussions/400 */
- markdocConfig as MarkdocConfig
- ).filter((e) => {
- return (
- // Ignore `variable-undefined` errors.
- // Variables can be configured at runtime,
- // so we cannot validate them at build time.
- e.error.id !== 'variable-undefined' &&
- (e.error.level === 'error' || e.error.level === 'critical')
- );
- });
- if (validationErrors.length) {
- // Heuristic: take number of newlines for `rawData` and add 2 for the `---` fences
- const frontmatterBlockOffset = entry.rawData.split('\n').length + 2;
- const rootRelativePath = path.relative(fileURLToPath(astroConfig.root), filePath);
- throw new MarkdocError({
- message: [
- `**${String(rootRelativePath)}** contains invalid content:`,
- ...validationErrors.map((e) => `- ${e.error.message}`),
- ].join('\n'),
- location: {
- // Error overlay does not support multi-line or ranges.
- // Just point to the first line.
- line: frontmatterBlockOffset + validationErrors[0].lines[0],
- file: viteId,
- },
- });
- }
-
await emitOptimizedImages(ast.children, {
astroConfig,
pluginContext,
@@ -142,6 +138,136 @@ export const Content = createContentComponent(
};
}
+/**
+ * Recursively resolve partial tags to their content.
+ * Note: Mutates the `ast` object directly.
+ */
+async function resolvePartials({
+ ast,
+ fileUrl,
+ root,
+ tokenizer,
+ allowHTML,
+ markdocConfig,
+ pluginContext,
+ raisePartialValidationErrors,
+}: {
+ ast: Node;
+ fileUrl: URL;
+ root: URL;
+ tokenizer: any;
+ allowHTML?: boolean;
+ markdocConfig: MarkdocConfig;
+ pluginContext: Rollup.PluginContext;
+ raisePartialValidationErrors: (ast: Node, filePath: string) => void;
+}) {
+ const relativePartialPath = path.relative(fileURLToPath(root), fileURLToPath(fileUrl));
+ for (const node of ast.walk()) {
+ if (node.type === 'tag' && node.tag === 'partial') {
+ const { file } = node.attributes;
+ if (!file) {
+ throw new MarkdocError({
+ // Should be caught by Markdoc validation step.
+ message: `(Uncaught error) Partial tag requires a 'file' attribute`,
+ });
+ }
+
+ if (markdocConfig.partials?.[file]) continue;
+
+ let partialPath: string;
+ let partialContents: string;
+ try {
+ const resolved = await pluginContext.resolve(file, fileURLToPath(fileUrl));
+ let partialId = resolved?.id;
+ if (!partialId) {
+ const attemptResolveAsRelative = await pluginContext.resolve(
+ './' + file,
+ fileURLToPath(fileUrl)
+ );
+ if (!attemptResolveAsRelative?.id) throw new Error();
+ partialId = attemptResolveAsRelative.id;
+ }
+
+ partialPath = fileURLToPath(new URL(prependForwardSlash(partialId), 'file://'));
+ partialContents = await fs.promises.readFile(partialPath, 'utf-8');
+ } catch {
+ throw new MarkdocError({
+ message: [
+ `**${String(relativePartialPath)}** contains invalid content:`,
+ `Could not read partial file \`${file}\`. Does the file exist?`,
+ ].join('\n'),
+ });
+ }
+ if (pluginContext.meta.watchMode) pluginContext.addWatchFile(partialPath);
+ let partialTokens = tokenizer.tokenize(partialContents);
+ if (allowHTML) {
+ partialTokens = htmlTokenTransform(tokenizer, partialTokens);
+ }
+ const partialAst = Markdoc.parse(partialTokens);
+ raisePartialValidationErrors(partialAst, partialPath);
+ await resolvePartials({
+ ast: partialAst,
+ root,
+ fileUrl: pathToFileURL(partialPath),
+ tokenizer,
+ allowHTML,
+ markdocConfig,
+ pluginContext,
+ raisePartialValidationErrors,
+ });
+
+ Object.assign(node, partialAst);
+ }
+ }
+}
+
+function raiseValidationErrors({
+ ast,
+ markdocConfig,
+ entry,
+ viteId,
+ astroConfig,
+ filePath,
+}: {
+ ast: Node;
+ markdocConfig: MarkdocConfig;
+ entry: ReturnType<typeof getEntryInfo>;
+ viteId: string;
+ astroConfig: AstroConfig;
+ filePath: string;
+}) {
+ const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => {
+ return (
+ (e.error.level === 'error' || e.error.level === 'critical') &&
+ // Ignore `variable-undefined` errors.
+ // Variables can be configured at runtime,
+ // so we cannot validate them at build time.
+ e.error.id !== 'variable-undefined' &&
+ // Ignore missing partial errors.
+ // We will resolve these in `resolvePartials`.
+ !(e.error.id === 'attribute-value-invalid' && e.error.message.match(/^Partial .+ not found/))
+ );
+ });
+
+ if (validationErrors.length) {
+ // Heuristic: take number of newlines for `rawData` and add 2 for the `---` fences
+ const frontmatterBlockOffset = entry.rawData.split('\n').length + 2;
+ const rootRelativePath = path.relative(fileURLToPath(astroConfig.root), filePath);
+ throw new MarkdocError({
+ message: [
+ `**${String(rootRelativePath)}** contains invalid content:`,
+ ...validationErrors.map((e) => `- ${e.error.message}`),
+ ].join('\n'),
+ location: {
+ // Error overlay does not support multi-line or ranges.
+ // Just point to the first line.
+ line: frontmatterBlockOffset + validationErrors[0].lines[0],
+ file: viteId,
+ },
+ });
+ }
+}
+
function getUsedTags(markdocAst: Node) {
const tags = new Set<string>();
const validationErrors = Markdoc.validate(markdocAst);
diff --git a/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/_partial.mdoc b/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/_partial.mdoc
new file mode 100644
index 000000000..f8774e911
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/_partial.mdoc
@@ -0,0 +1,5 @@
+## HTML in a partial
+
+<ul>
+ <li id="partial">List item</li>
+</ul>
diff --git a/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/with-partial.mdoc b/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/with-partial.mdoc
new file mode 100644
index 000000000..c42d3cd70
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-html/src/content/blog/with-partial.mdoc
@@ -0,0 +1,5 @@
+---
+title: With Partial
+---
+
+{% partial file="./_partial.mdoc" /%}
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/render-partials/astro.config.mjs
new file mode 100644
index 000000000..1bd8ba93f
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/astro.config.mjs
@@ -0,0 +1,7 @@
+import markdoc from '@astrojs/markdoc';
+import { defineConfig } from 'astro/config';
+
+// https://astro.build/config
+export default defineConfig({
+ integrations: [markdoc()],
+});
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/markdoc.config.ts b/packages/integrations/markdoc/test/fixtures/render-partials/markdoc.config.ts
new file mode 100644
index 000000000..c9762aed5
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/markdoc.config.ts
@@ -0,0 +1,7 @@
+import { Markdoc, defineMarkdocConfig } from '@astrojs/markdoc/config';
+
+export default defineMarkdocConfig({
+ partials: {
+ configured: Markdoc.parse('# Configured partial {% #configured %}'),
+ },
+});
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/package.json b/packages/integrations/markdoc/test/fixtures/render-partials/package.json
new file mode 100644
index 000000000..021e1c2d9
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/markdoc-render-partials",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/markdoc": "workspace:*",
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/_partial.mdoc b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/_partial.mdoc
new file mode 100644
index 000000000..4ace9a9d3
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/_partial.mdoc
@@ -0,0 +1,3 @@
+## Partial {% #top %}
+
+{% partial file="../nested/_partial.mdoc" /%}
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/with-partials.mdoc b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/with-partials.mdoc
new file mode 100644
index 000000000..2d9a87110
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/blog/with-partials.mdoc
@@ -0,0 +1,7 @@
+---
+title: Post with partials
+---
+
+{% partial file="_partial.mdoc" /%}
+
+{% partial file="configured" /%}
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/src/content/nested/_partial.mdoc b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/nested/_partial.mdoc
new file mode 100644
index 000000000..4193609bf
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/src/content/nested/_partial.mdoc
@@ -0,0 +1 @@
+## Nested partial {% #nested %}
diff --git a/packages/integrations/markdoc/test/fixtures/render-partials/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/render-partials/src/pages/index.astro
new file mode 100644
index 000000000..e9549f314
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-partials/src/pages/index.astro
@@ -0,0 +1,19 @@
+---
+import { getEntryBySlug } from 'astro:content';
+
+const post = await getEntryBySlug('blog', 'with-partials');
+const { Content } = await post.render();
+---
+
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Content</title>
+ </head>
+ <body>
+ <Content />
+ </body>
+</html>
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs
index 1bd8ba93f..a5d98b012 100644
--- a/packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/astro.config.mjs
@@ -1,7 +1,8 @@
import markdoc from '@astrojs/markdoc';
import { defineConfig } from 'astro/config';
+import preact from '@astrojs/preact';
// https://astro.build/config
export default defineConfig({
- integrations: [markdoc()],
+ integrations: [markdoc(), preact()],
});
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.ts b/packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.ts
index 2016327a8..6093ec593 100644
--- a/packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.ts
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/markdoc.config.ts
@@ -1,4 +1,4 @@
-import { component, defineMarkdocConfig } from '@astrojs/markdoc/config';
+import { Markdoc, component, defineMarkdocConfig } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
@@ -22,5 +22,11 @@ export default defineMarkdocConfig({
},
},
},
+ counter: {
+ render: component('./src/components/CounterWrapper.astro'),
+ },
+ 'deeply-nested': {
+ render: component('./src/components/DeeplyNested.astro'),
+ },
},
-})
+});
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/package.json b/packages/integrations/markdoc/test/fixtures/render-with-components/package.json
index b81033473..f70e2b6c1 100644
--- a/packages/integrations/markdoc/test/fixtures/render-with-components/package.json
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/package.json
@@ -4,6 +4,8 @@
"private": true,
"dependencies": {
"@astrojs/markdoc": "workspace:*",
- "astro": "workspace:*"
+ "@astrojs/preact": "workspace:*",
+ "astro": "workspace:*",
+ "preact": "^10.20.1"
}
}
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/Counter.tsx b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/Counter.tsx
new file mode 100644
index 000000000..f1e239718
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/Counter.tsx
@@ -0,0 +1,10 @@
+import { useState } from 'preact/hooks';
+
+export default function Counter() {
+ const [count, setCount] = useState(1);
+ return (
+ <button id="counter" onClick={() => setCount(count + 1)}>
+ {count}
+ </button>
+ );
+}
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/CounterWrapper.astro b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/CounterWrapper.astro
new file mode 100644
index 000000000..e45ac6438
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/CounterWrapper.astro
@@ -0,0 +1,5 @@
+---
+import Counter from './Counter';
+---
+
+<Counter client:load />
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/DeeplyNested.astro b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/DeeplyNested.astro
new file mode 100644
index 000000000..eb23f675a
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/components/DeeplyNested.astro
@@ -0,0 +1,5 @@
+---
+
+---
+
+<p id="deeply-nested">Deeply nested partial</p>
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/_nested.mdoc b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/_nested.mdoc
new file mode 100644
index 000000000..68f529280
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/_nested.mdoc
@@ -0,0 +1,3 @@
+Render components from a deeply nested partial:
+
+{% deeply-nested /%}
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/_counter.mdoc b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/_counter.mdoc
new file mode 100644
index 000000000..4a015695c
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/_counter.mdoc
@@ -0,0 +1,7 @@
+# Hello from a partial!
+
+Render a component from a partial:
+
+{% counter /%}
+
+{% partial file="../_nested.mdoc" /%}
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/with-components.mdoc b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/with-components.mdoc
index 61f404a97..eb7d20426 100644
--- a/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/with-components.mdoc
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/src/content/blog/with-components.mdoc
@@ -10,6 +10,8 @@ This uses a custom marquee component with a shortcode:
I'm a marquee too!
{% /marquee-element %}
+{% partial file="_counter.mdoc" /%}
+
And a code component for code blocks:
```js
diff --git a/packages/integrations/markdoc/test/fixtures/render-with-components/tsconfig.json b/packages/integrations/markdoc/test/fixtures/render-with-components/tsconfig.json
new file mode 100644
index 000000000..99df2e61a
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/render-with-components/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "astro/tsconfigs/base",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "jsxImportSource": "preact"
+ }
+} \ No newline at end of file
diff --git a/packages/integrations/markdoc/test/render-components.test.js b/packages/integrations/markdoc/test/render-components.test.js
new file mode 100644
index 000000000..5639770ab
--- /dev/null
+++ b/packages/integrations/markdoc/test/render-components.test.js
@@ -0,0 +1,89 @@
+import assert from 'node:assert/strict';
+import { describe, it, before, after } from 'node:test';
+import { parseHTML } from 'linkedom';
+import { loadFixture } from '../../../astro/test/test-utils.js';
+
+const root = new URL('./fixtures/render-with-components/', import.meta.url);
+
+describe('Markdoc - render components', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root,
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
+ });
+
+ it('renders content - with components', async () => {
+ const res = await fixture.fetch('/');
+ const html = await res.text();
+
+ renderComponentsChecks(html);
+ });
+
+ it('renders content - with components inside partials', async () => {
+ const res = await fixture.fetch('/');
+ const html = await res.text();
+
+ renderComponentsInsidePartialsChecks(html);
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ it('renders content - with components', async () => {
+ const html = await fixture.readFile('/index.html');
+
+ renderComponentsChecks(html);
+ });
+
+ it('renders content - with components inside partials', async () => {
+ const html = await fixture.readFile('/index.html');
+
+ renderComponentsInsidePartialsChecks(html);
+ });
+ });
+});
+
+/** @param {string} html */
+function renderComponentsChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ assert.equal(h2.textContent, 'Post with components');
+
+ // Renders custom shortcode component
+ const marquee = document.querySelector('marquee');
+ assert.notEqual(marquee, null);
+ assert.equal(marquee.hasAttribute('data-custom-marquee'), true);
+
+ // Renders Astro Code component
+ const pre = document.querySelector('pre');
+ assert.notEqual(pre, null);
+ assert.equal(pre.className, 'astro-code github-dark');
+}
+
+/** @param {string} html */
+function renderComponentsInsidePartialsChecks(html) {
+ const { document } = parseHTML(html);
+ // renders Counter.tsx
+ const button = document.querySelector('#counter');
+ assert.equal(button.textContent, '1');
+
+ // renders DeeplyNested.astro
+ const deeplyNested = document.querySelector('#deeply-nested');
+ assert.equal(deeplyNested.textContent, 'Deeply nested partial');
+}
diff --git a/packages/integrations/markdoc/test/render-html.test.js b/packages/integrations/markdoc/test/render-html.test.js
index 785599ae5..4780444bf 100644
--- a/packages/integrations/markdoc/test/render-html.test.js
+++ b/packages/integrations/markdoc/test/render-html.test.js
@@ -54,6 +54,13 @@ describe('Markdoc - render html', () => {
renderRandomlyCasedHTMLAttributesChecks(html);
});
+
+ it('renders content - html within partials', async () => {
+ const res = await fixture.fetch('/with-partial');
+ const html = await res.text();
+
+ renderHTMLWithinPartialChecks(html);
+ });
});
describe('build', () => {
@@ -84,6 +91,12 @@ describe('Markdoc - render html', () => {
renderRandomlyCasedHTMLAttributesChecks(html);
});
+
+ it('renders content - html within partials', async () => {
+ const html = await fixture.readFile('/with-partial/index.html');
+
+ renderHTMLWithinPartialChecks(html);
+ });
});
});
@@ -187,6 +200,16 @@ function renderRandomlyCasedHTMLAttributesChecks(html) {
}
/**
+ * @param {string} html
+ */
+function renderHTMLWithinPartialChecks(html) {
+ const { document } = parseHTML(html);
+
+ const li = document.querySelector('ul > li#partial');
+ assert.equal(li.textContent, 'List item');
+}
+
+/**
* Asserts that the rendered HTML tags with interleaved Markdoc tags (both block and inline) rendered in the expected nested graph of elemements
*
* @param {string} html */
diff --git a/packages/integrations/markdoc/test/render-indented-components.test.js b/packages/integrations/markdoc/test/render-indented-components.test.js
new file mode 100644
index 000000000..60b2a4ab5
--- /dev/null
+++ b/packages/integrations/markdoc/test/render-indented-components.test.js
@@ -0,0 +1,67 @@
+import assert from 'node:assert/strict';
+import { describe, it, before, after } from 'node:test';
+import { parseHTML } from 'linkedom';
+import { loadFixture } from '../../../astro/test/test-utils.js';
+
+const root = new URL('./fixtures/render-with-indented-components/', import.meta.url);
+
+describe('Markdoc - render indented components', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root,
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
+ });
+
+ it('renders content - with indented components', async () => {
+ const res = await fixture.fetch('/');
+ const html = await res.text();
+
+ renderIndentedComponentsChecks(html);
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ it('renders content - with indented components', async () => {
+ const html = await fixture.readFile('/index.html');
+
+ renderIndentedComponentsChecks(html);
+ });
+ });
+});
+
+/** @param {string} html */
+function renderIndentedComponentsChecks(html) {
+ const { document } = parseHTML(html);
+ const h2 = document.querySelector('h2');
+ assert.equal(h2.textContent, 'Post with indented components');
+
+ // Renders custom shortcode components
+ const marquees = document.querySelectorAll('marquee');
+ assert.equal(marquees.length, 2);
+
+ // Renders h3
+ const h3 = document.querySelector('h3');
+ assert.equal(h3.textContent, 'I am an h3!');
+
+ // Renders Astro Code component
+ const pre = document.querySelector('pre');
+ assert.notEqual(pre, null);
+ assert.equal(pre.className, 'astro-code github-dark');
+}
diff --git a/packages/integrations/markdoc/test/render.test.js b/packages/integrations/markdoc/test/render.test.js
index 0960a980c..d439adcd2 100644
--- a/packages/integrations/markdoc/test/render.test.js
+++ b/packages/integrations/markdoc/test/render.test.js
@@ -23,38 +23,26 @@ describe('Markdoc - render', () => {
await server.stop();
});
- it('renders content - with config', async () => {
- const fixture = await getFixture('render-with-config');
+ it('renders content - with partials', async () => {
+ const fixture = await getFixture('render-partials');
const server = await fixture.startDevServer();
const res = await fixture.fetch('/');
const html = await res.text();
- renderConfigChecks(html);
+ renderPartialsChecks(html);
await server.stop();
});
- it('renders content - with components', async () => {
- const fixture = await getFixture('render-with-components');
- const server = await fixture.startDevServer();
-
- const res = await fixture.fetch('/');
- const html = await res.text();
-
- renderComponentsChecks(html);
-
- await server.stop();
- });
-
- it('renders content - with indented components', async () => {
- const fixture = await getFixture('render-with-indented-components');
+ it('renders content - with config', async () => {
+ const fixture = await getFixture('render-with-config');
const server = await fixture.startDevServer();
const res = await fixture.fetch('/');
const html = await res.text();
- renderIndentedComponentsChecks(html);
+ renderConfigChecks(html);
await server.stop();
});
@@ -94,31 +82,22 @@ describe('Markdoc - render', () => {
renderSimpleChecks(html);
});
- it('renders content - with config', async () => {
- const fixture = await getFixture('render-with-config');
- await fixture.build();
-
- const html = await fixture.readFile('/index.html');
-
- renderConfigChecks(html);
- });
-
- it('renders content - with components', async () => {
- const fixture = await getFixture('render-with-components');
+ it('renders content - with partials', async () => {
+ const fixture = await getFixture('render-partials');
await fixture.build();
const html = await fixture.readFile('/index.html');
- renderComponentsChecks(html);
+ renderPartialsChecks(html);
});
- it('renders content - with indented components', async () => {
- const fixture = await getFixture('render-with-indented-components');
+ it('renders content - with config', async () => {
+ const fixture = await getFixture('render-with-config');
await fixture.build();
const html = await fixture.readFile('/index.html');
- renderIndentedComponentsChecks(html);
+ renderConfigChecks(html);
});
it('renders content - with `render: null` in document', async () => {
@@ -152,40 +131,14 @@ function renderNullChecks(html) {
}
/** @param {string} html */
-function renderComponentsChecks(html) {
+function renderPartialsChecks(html) {
const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- assert.equal(h2.textContent, 'Post with components');
-
- // Renders custom shortcode component
- const marquee = document.querySelector('marquee');
- assert.notEqual(marquee, null);
- assert.equal(marquee.hasAttribute('data-custom-marquee'), true);
-
- // Renders Astro Code component
- const pre = document.querySelector('pre');
- assert.notEqual(pre, null);
- assert.equal(pre.className, 'astro-code github-dark');
-}
-
-/** @param {string} html */
-function renderIndentedComponentsChecks(html) {
- const { document } = parseHTML(html);
- const h2 = document.querySelector('h2');
- assert.equal(h2.textContent, 'Post with indented components');
-
- // Renders custom shortcode components
- const marquees = document.querySelectorAll('marquee');
- assert.equal(marquees.length, 2);
-
- // Renders h3
- const h3 = document.querySelector('h3');
- assert.equal(h3.textContent, 'I am an h3!');
-
- // Renders Astro Code component
- const pre = document.querySelector('pre');
- assert.notEqual(pre, null);
- assert.equal(pre.className, 'astro-code github-dark');
+ const top = document.querySelector('#top');
+ assert.ok(top);
+ const nested = document.querySelector('#nested');
+ assert.ok(nested);
+ const configured = document.querySelector('#configured');
+ assert.ok(configured);
}
/** @param {string} html */