summaryrefslogtreecommitdiff
path: root/packages/markdown
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@skypack.dev> 2022-07-20 16:45:05 -0400
committerGravatar GitHub <noreply@github.com> 2022-07-20 16:45:05 -0400
commitbccd88f0ebe1fbf383c0cee4b27a4c24c72dea72 (patch)
tree6ba2a85c65fac8ee0fd7d99c4c7f1b715789d76d /packages/markdown
parentd13afad272bd558efadbc64de29f307bd58d8de1 (diff)
downloadastro-bccd88f0ebe1fbf383c0cee4b27a4c24c72dea72.tar.gz
astro-bccd88f0ebe1fbf383c0cee4b27a4c24c72dea72.tar.zst
astro-bccd88f0ebe1fbf383c0cee4b27a4c24c72dea72.zip
Move the Markdown component to its own package (#3986)
* Move the Markdown component to its own package * Update the examples * Updated lockfile * Use is:raw * Add a main field * Update the formatting of the readme * Rename to @astrojs/markdown-component
Diffstat (limited to 'packages/markdown')
-rw-r--r--packages/markdown/component/Markdown.astro54
-rw-r--r--packages/markdown/component/package.json31
-rw-r--r--packages/markdown/component/readme.md17
-rw-r--r--packages/markdown/component/test/astro-markdown-plugins.test.js37
-rw-r--r--packages/markdown/component/test/astro-markdown-shiki.test.js147
-rw-r--r--packages/markdown/component/test/astro-markdown.test.js350
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-plugins/add-classes.mjs18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-plugins/astro.config.mjs7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-plugins/package.json12
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-plugins/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/astro.config.mjs19
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro31
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/astro.config.mjs5
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/astro.config.mjs10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro14
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/astro.config.mjs6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro14
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/index.md24
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/astro.config.mjs6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/astro.config.mjs6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/astro.config.mjs6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/package.json9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/package.json11
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/Counter.jsx7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/Example.jsx5
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/Hello.jsx5
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/SlotComponent.astro13
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/SvelteButton.svelte11
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/TextBlock.jsx20
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/components/index.js5
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/content/code-element.md3
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/plain.md6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/with-components.md17
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/layouts/content.astro10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/layouts/layout-props.astro17
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/braces.astro13
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/children.md12
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/close.astro12
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-element.astro7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-in-md.md16
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro13
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment-with-js.md23
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment.md2
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro18
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/content.astro6
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/dash.md14
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro29
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/empty-code.md20
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/external.astro19
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro9
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/jsx-expressions.md13
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/layout-props.md4
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/namespace.md7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro32
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro5
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/post.astro16
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/raw-content.json.js10
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro21
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/script.md7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/slots.md38
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/slug.md7
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/pages/vite-env-vars.md35
-rw-r--r--packages/markdown/component/test/fixtures/astro-markdown/src/scripts/test.js1
-rw-r--r--packages/markdown/component/test/test-utils.js12
84 files changed, 1608 insertions, 0 deletions
diff --git a/packages/markdown/component/Markdown.astro b/packages/markdown/component/Markdown.astro
new file mode 100644
index 000000000..a88fdbf34
--- /dev/null
+++ b/packages/markdown/component/Markdown.astro
@@ -0,0 +1,54 @@
+---
+export interface Props {
+ content?: string;
+}
+
+// NOTE(fks): We are most likely moving this component out of Astro core
+// in a few weeks. Checking the name like this is a bit of a hack, but we
+// intentionally don't want to add an SSR flag for others to read from, just yet.
+if (Astro.redirect.name !== '_onlyAvailableInSSR') {
+ console.error(`\x1B[31mThe <Markdown> component is not available in SSR. See https://github.com/withastro/rfcs/discussions/179 for more info.\x1B[39m`);
+}
+
+const dedent = (str: string) => {
+ const _str = str.split('\n').filter(s => s.trimStart().length > 0);
+ if (_str.length === 0) {
+ return str.trimStart();
+ }
+ const trimmedSpace = _str[0].replace(_str[0].trimStart(), '');
+ return str
+ .split('\n')
+ .map((ln) => ln.startsWith(trimmedSpace) ? ln.replace(trimmedSpace, '') : ln)
+ .join('\n');
+}
+
+// Internal props that should not be part of the external interface.
+interface InternalProps extends Props {
+ $scope: string;
+}
+
+let { content, class: className } = Astro.props as InternalProps;
+let html = null;
+let htmlContent = '';
+
+// If no content prop provided, use the slot.
+if (!content) {
+ content = await Astro.slots.render('default');
+ if (content !== undefined && content !== null) {
+ content = dedent(content);
+ }
+}
+
+if (content) {
+ htmlContent = await (Astro as any).__renderMarkdown(content, {
+ mode: 'md',
+ $: {
+ scopedClassName: className,
+ },
+ });
+}
+
+html = htmlContent;
+---
+
+{html ? <Fragment set:html={html} /> : <slot />}
diff --git a/packages/markdown/component/package.json b/packages/markdown/component/package.json
new file mode 100644
index 000000000..5308899e0
--- /dev/null
+++ b/packages/markdown/component/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@astrojs/markdown-component",
+ "version": "0.1.0",
+ "type": "module",
+ "author": "withastro",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/withastro/astro.git",
+ "directory": "packages/markdown/component"
+ },
+ "bugs": "https://github.com/withastro/astro/issues",
+ "homepage": "https://astro.build",
+ "main": "./Markdown.astro",
+ "exports": {
+ ".": {
+ "astro": "./Markdown.astro"
+ }
+ },
+ "scripts": {
+ "test": "mocha --exit --timeout 20000"
+ },
+ "devDependencies": {
+ "astro": "workspace:*",
+ "chai": "^4.3.6",
+ "cheerio": "^1.0.0-rc.11",
+ "mocha": "^9.2.2",
+ "@types/mocha": "^9.1.1"
+ },
+ "keywords": ["astro", "astro-component"]
+}
diff --git a/packages/markdown/component/readme.md b/packages/markdown/component/readme.md
new file mode 100644
index 000000000..aa48e274d
--- /dev/null
+++ b/packages/markdown/component/readme.md
@@ -0,0 +1,17 @@
+# @astrojs/markdown
+
+This package brings legacy support for the `<Markdown />` component to all Astro projects.
+
+> The `<Markdown />` component does not work in SSR. Consider [importing Markdown content](https://docs.astro.build/en/guides/markdown-content/#importing-markdown) instead.
+:::
+
+```astro
+---
+import Markdown from '@astrojs/markdown-component';
+---
+<Markdown>
+ # Markdown syntax is now supported! **Yay!**
+</Markdown>
+```
+
+See our [Markdown Guide](https://docs.astro.build/en/guides/markdown-content/) for more info.
diff --git a/packages/markdown/component/test/astro-markdown-plugins.test.js b/packages/markdown/component/test/astro-markdown-plugins.test.js
new file mode 100644
index 000000000..c748d81c9
--- /dev/null
+++ b/packages/markdown/component/test/astro-markdown-plugins.test.js
@@ -0,0 +1,37 @@
+import { expect } from 'chai';
+import * as cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
+import addClasses from './fixtures/astro-markdown-plugins/add-classes.mjs';
+
+describe('Astro Markdown plugins', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/astro-markdown-plugins/',
+ markdown: {
+ remarkPlugins: [
+ 'remark-code-titles',
+ ['rehype-autolink-headings', { behavior: 'prepend' }],
+ ],
+ rehypePlugins: [
+ 'rehype-slug',
+ ['rehype-toc', { headings: ['h2', 'h3'] }],
+ [addClasses, { 'h1,h2,h3': 'title' }],
+ ],
+ },
+ });
+ await fixture.build();
+ });
+
+ it('Can render Astro <Markdown> with plugins', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Added a TOC
+ expect($('.toc')).to.have.lengthOf(1);
+
+ // teste 2: Added .title to h1
+ expect($('#hello-world').hasClass('title')).to.equal(true);
+ });
+});
diff --git a/packages/markdown/component/test/astro-markdown-shiki.test.js b/packages/markdown/component/test/astro-markdown-shiki.test.js
new file mode 100644
index 000000000..fb4f8f962
--- /dev/null
+++ b/packages/markdown/component/test/astro-markdown-shiki.test.js
@@ -0,0 +1,147 @@
+import { expect } from 'chai';
+import * as cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
+
+describe('Astro Markdown Shiki', () => {
+ describe('Render shiki', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/normal/' });
+ await fixture.build();
+ });
+
+ it('Can render Astro <Markdown> with shiki', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ // There should be no HTML from Prism
+ expect($('.token')).to.have.lengthOf(0);
+
+ expect($('pre')).to.have.lengthOf(2);
+
+ expect($('span.line')).to.have.lengthOf(2);
+ expect($('span.line').get(0).children).to.have.lengthOf(1);
+ expect($('span.line').get(1).children).to.have.lengthOf(5);
+ });
+ });
+
+ describe('Themes', () => {
+ describe('Integrated theme', async () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/themes-integrated/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').hasClass('astro-code')).to.equal(true);
+ expect($('pre').attr().style).to.equal('background-color: #ffffff; overflow-x: auto;');
+ });
+ });
+
+ describe('Custom theme', async () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/themes-custom/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre')).to.have.lengthOf(1);
+ expect($('pre').hasClass('astro-code')).to.equal(true);
+ expect($('pre').attr().style).to.equal('background-color: #FDFDFE; overflow-x: auto;');
+ });
+ });
+ });
+
+ describe('Custom langs', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/langs/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ const segments = $('.line').get(6).children;
+ expect(segments).to.have.lengthOf(3);
+ expect(segments[0].attribs.style).to.be.equal('color: #C9D1D9');
+ expect(segments[1].attribs.style).to.be.equal('color: #79C0FF');
+
+ const unknownLang = $('.line').last().html();
+ expect(unknownLang).to.be.equal(
+ '<span style="color: #c9d1d9">This language does not exist</span>'
+ );
+ });
+ });
+
+ describe('Wrap', () => {
+ describe('wrap = true', () => {
+ const style =
+ 'background-color: #0d1117; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;';
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-true/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre').get(0).attribs.style).to.equal(style);
+ expect($('pre').get(1).attribs.style).to.equal(style);
+ });
+ });
+ });
+
+ describe('wrap = false', () => {
+ const style = 'background-color: #0d1117; overflow-x: auto;';
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-false/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre').get(0).attribs.style).to.equal(style);
+ expect($('pre').get(1).attribs.style).to.equal(style);
+ });
+ });
+
+ describe('wrap = null', () => {
+ const style = 'background-color: #0d1117';
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: './fixtures/astro-markdown-shiki/wrap-null/' });
+ await fixture.build();
+ });
+
+ it('<Markdown /> component', async () => {
+ const html = await fixture.readFile('/astro/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre').get(0).attribs.style).to.equal(style);
+ expect($('pre').get(1).attribs.style).to.equal(style);
+ });
+ });
+});
diff --git a/packages/markdown/component/test/astro-markdown.test.js b/packages/markdown/component/test/astro-markdown.test.js
new file mode 100644
index 000000000..c0726d2ca
--- /dev/null
+++ b/packages/markdown/component/test/astro-markdown.test.js
@@ -0,0 +1,350 @@
+import { expect } from 'chai';
+import * as cheerio from 'cheerio';
+import { loadFixture, fixLineEndings } from './test-utils.js';
+
+describe('Astro Markdown', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/astro-markdown/',
+ });
+ await fixture.build();
+ });
+
+ it('Can load markdown pages with Astro', async () => {
+ const html = await fixture.readFile('/post/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: There is a div added in markdown
+ expect($('#first').length).to.be.ok;
+
+ // test 2: There is a div added via a component from markdown
+ expect($('#test').length).to.be.ok;
+ });
+
+ it('Can parse JSX expressions in markdown pages', async () => {
+ const html = await fixture.readFile('/jsx-expressions/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h2').html()).to.equal('Blog Post with JSX expressions');
+
+ expect(html).to.contain('JSX at the start of the line!');
+ for (let listItem of ['test-1', 'test-2', 'test-3']) {
+ expect($(`#${listItem}`).html()).to.equal(`${listItem}`);
+ }
+ });
+
+ it('Can handle slugs with JSX expressions in markdown pages', async () => {
+ const html = await fixture.readFile('/slug/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h1').attr('id')).to.equal('my-blog-post');
+ });
+
+ it('Can handle code elements without extra spacing', async () => {
+ const html = await fixture.readFile('/code-element/index.html');
+ const $ = cheerio.load(html);
+
+ $('code').each((_, el) => {
+ expect($(el).html()).to.equal($(el).html().trim());
+ });
+ });
+
+ it('Can handle namespaced components in markdown', async () => {
+ const html = await fixture.readFile('/namespace/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h1').text()).to.equal('Hello Namespace!');
+ expect($('button').length).to.equal(1);
+ });
+
+ it('Correctly handles component children in markdown pages (#3319)', async () => {
+ const html = await fixture.readFile('/children/index.html');
+
+ expect(html).not.to.contain('<p></p>');
+ });
+
+ it('Can handle HTML comments in markdown pages', async () => {
+ const html = await fixture.readFile('/comment/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h1').text()).to.equal('It works!');
+ });
+
+ it('Prevents `*/` sequences from breaking HTML comments (#3476)', async () => {
+ const html = await fixture.readFile('/comment-with-js/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('h1').text()).to.equal('It still works!');
+ });
+
+ it('Can handle HTML comments in inline code', async () => {
+ const html = await fixture.readFile('/comment-with-js/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('p code').text()).to.equal('<!-- HTML comments in code -->');
+ });
+
+ it('Can handle HTML comments in code fences', async () => {
+ const html = await fixture.readFile('/comment-with-js/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('pre > code').text()).to.equal('<!-- HTML comments in code fence -->');
+ });
+
+ // https://github.com/withastro/astro/issues/3254
+ it('Can handle scripts in markdown pages', async () => {
+ const html = await fixture.readFile('/script/index.html');
+ expect(html).not.to.match(new RegExp('/src/scripts/test.js'));
+ });
+
+ it('Can load more complex jsxy stuff', async () => {
+ const html = await fixture.readFile('/complex/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#test').text()).to.equal('Hello world');
+ });
+
+ it('Empty code blocks do not fail', async () => {
+ const html = await fixture.readFile('/empty-code/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: There is not a `<code>` in the codeblock
+ expect($('pre')[0].children).to.have.lengthOf(1);
+
+ // test 2: The empty `<pre>` failed to render
+ expect($('pre')[1].children).to.have.lengthOf(0);
+ });
+
+ it('Runs code blocks through syntax highlighter', async () => {
+ const html = await fixture.readFile('/code/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: There are child spans in code blocks
+ expect($('code span').length).greaterThan(0);
+ });
+
+ it('Scoped styles should not break syntax highlight', async () => {
+ const html = await fixture.readFile('/scopedStyles-code/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: <pre> tag has correct shiki class
+ expect($('pre').hasClass('astro-code')).to.equal(true);
+
+ // test 2: inline styles are still applied
+ expect($('pre').is('[style]')).to.equal(true);
+
+ // test 3: There are styled child spans in code blocks
+ expect($('pre code span').length).to.be.greaterThan(0);
+ expect($('pre code span').is('[style]')).to.equal(true);
+ });
+
+ function isAstroScopedClass(cls) {
+ return /^astro-.*/.test(cls);
+ }
+
+ it('Scoped styles should be applied to syntax highlighted lines', async () => {
+ const html = await fixture.readFile('/scopedStyles-code/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: the "pre" tag receives scoped style
+ const preClassList = $('pre').attr('class').split(/\s+/);
+ expect(preClassList.length).to.equal(2);
+ const preAstroClass = preClassList.find(isAstroScopedClass);
+ expect(Boolean(preAstroClass)).to.equal(true);
+
+ // test 2: each "span" line receives scoped style
+ const spanClassList = $('pre code span').attr('class').split(/\s+/);
+ expect(spanClassList.length).to.equal(2);
+ const spanAstroClass = spanClassList.find(isAstroScopedClass);
+ expect(Boolean(spanAstroClass)).to.equal(true);
+ });
+
+ it('Renders correctly when deeply nested on a page', async () => {
+ const html = await fixture.readFile('/deep/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Rendered all children
+ expect($('#deep').children()).to.have.lengthOf(3);
+
+ // tests 2–4: Only rendered title in each section
+ expect($('.a').children()).to.have.lengthOf(1);
+ expect($('.b').children()).to.have.lengthOf(1);
+ expect($('.c').children()).to.have.lengthOf(1);
+
+ // test 5–7: Rendered title in correct section
+ expect($('.a > h2').text()).to.equal('A');
+ expect($('.b > h2').text()).to.equal('B');
+ expect($('.c > h2').text()).to.equal('C');
+ });
+
+ it('Renders dynamic content though the content attribute', async () => {
+ const html = await fixture.readFile('/external/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Rendered markdown content
+ expect($('#outer')).to.have.lengthOf(1);
+
+ // test 2: Nested markdown content
+ expect($('#inner')).to.have.lengthOf(1);
+
+ // test 3: Scoped class passed down
+ expect($('#inner').is('[class]')).to.equal(true);
+ });
+
+ it('Renders curly braces correctly', async () => {
+ const html = await fixture.readFile('/braces/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: Rendered curly braces markdown content
+ expect($('code')).to.have.lengthOf(3);
+
+ // test 2: Rendered curly braces markdown content
+ expect($('code:first-child').text()).to.equal('({})');
+
+ // test 3: Rendered curly braces markdown content
+ expect($('code:nth-child(2)').text()).to.equal('{...props}');
+
+ // test 4: Rendered curly braces markdown content
+ expect($('code:last-child').text()).to.equal('{/* JavaScript */}');
+ });
+
+ it('Does not close parent early when using content attribute (#494)', async () => {
+ const html = await fixture.readFile('/close/index.html');
+ const $ = cheerio.load(html);
+
+ // test <Markdown content /> closed div#target early
+ expect($('#target').children()).to.have.lengthOf(2);
+ });
+
+ it('Can render markdown with --- for horizontal rule', async () => {
+ const html = await fixture.readFile('/dash/index.html');
+ expect(!!html).to.equal(true);
+ });
+
+ it('Can render markdown content prop (#1259)', async () => {
+ const html = await fixture.readFile('/content/index.html');
+ const $ = cheerio.load(html);
+
+ // test Markdown rendered correctly via content prop
+ expect($('h1').text()).to.equal('Foo');
+ });
+
+ it("doesn't occurs TypeError when no elements", async () => {
+ const html = await fixture.readFile('/no-elements/index.html');
+ // render html without error
+ expect(html).to.be.ok;
+ });
+
+ it('can render nested list correctly', async () => {
+ const html = await fixture.readFile('/nested-list/index.html');
+ const $ = cheerio.load(html);
+ /**
+ * - list
+ * - list
+ */
+ expect($('#target > ul > li').children()).to.have.lengthOf(1);
+ expect($('#target > ul > li > ul > li').text()).to.equal('nested list');
+ /**
+ * 1. Hello
+ * 1. nested hello
+ */
+ expect($('#target > ol > li').children()).to.have.lengthOf(1);
+ expect($('#target > ol > li > ol > li').text()).to.equal('nested hello');
+ });
+
+ it('Exposes raw markdown content', async () => {
+ const { raw } = JSON.parse(await fixture.readFile('/raw-content.json'));
+
+ expect(fixLineEndings(raw)).to.equal(
+ `\n## With components\n\n### Non-hydrated\n\n<Hello name="Astro Naut" />\n\n### Hydrated\n\n<Counter client:load />\n<SvelteButton client:load />\n`
+ );
+ });
+
+ it('Exposes HTML parser for raw markdown content', async () => {
+ const { compiled } = JSON.parse(await fixture.readFile('/raw-content.json'));
+
+ expect(fixLineEndings(compiled)).to.equal(
+ `<h2 id="with-components">With components</h2>\n<h3 id="non-hydrated">Non-hydrated</h3>\n<Hello name="Astro Naut" />\n<h3 id="hydrated">Hydrated</h3>\n<Counter client:load />\n<SvelteButton client:load />`
+ );
+ });
+
+ it('Allows referencing Vite env var names in markdown (#3412)', async () => {
+ const html = await fixture.readFile('/vite-env-vars/index.html');
+ const $ = cheerio.load(html);
+
+ // test 1: referencing an existing var name
+ expect($('code').eq(0).text()).to.equal('import.meta.env.SITE');
+ expect($('li').eq(0).text()).to.equal('import.meta.env.SITE');
+ expect($('code').eq(3).text()).to.contain('site: import.meta.env.SITE');
+ expect($('blockquote').text()).to.contain('import.meta.env.SITE');
+
+ // test 2: referencing a non-existing var name
+ expect($('code').eq(1).text()).to.equal('import.meta.env.TITLE');
+ expect($('li').eq(1).text()).to.equal('import.meta.env.TITLE');
+ expect($('code').eq(3).text()).to.contain('title: import.meta.env.TITLE');
+ expect($('blockquote').text()).to.contain('import.meta.env.TITLE');
+
+ // test 3: referencing `import.meta.env` itself (without any var name)
+ expect($('code').eq(2).text()).to.equal('import.meta.env');
+ expect($('li').eq(2).text()).to.equal('import.meta.env');
+ expect($('code').eq(3).text()).to.contain('// Use Vite env vars with import.meta.env');
+ expect($('blockquote').text()).to.match(/import\.meta\.env\s*$/);
+ });
+
+ it('Escapes HTML tags in code blocks', async () => {
+ const html = await fixture.readFile('/code-in-md/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('code').eq(0).html()).to.equal('&lt;script&gt;');
+ expect($('blockquote').length).to.equal(1);
+ expect($('code').eq(1).html()).to.equal('&lt;/script&gt;');
+ expect($('pre').html()).to.contain('&gt;This should also work without any problems.&lt;');
+ });
+
+ it('Allows defining slot contents in component children', async () => {
+ const html = await fixture.readFile('/slots/index.html');
+ const $ = cheerio.load(html);
+
+ const slots = $('article').eq(0);
+ expect(slots.find('> .fragmentSlot > div').text()).to.contain('1:');
+ expect(slots.find('> .fragmentSlot > div + p').text()).to.contain('2:');
+ expect(slots.find('> .pSlot > p[title="hello"]').text()).to.contain('3:');
+ expect(slots.find('> .defaultSlot').html()).to.match(
+ new RegExp(
+ `<div>4: Div in default slot</div>` +
+ // Optional extra paragraph due to the line breaks between components
+ `(<p></p>)?` +
+ `<p>5: Paragraph in fragment in default slot</p>` +
+ // Optional whitespace due to the line breaks between components
+ `[\s\n]*` +
+ `6: Regular text in default slot`
+ )
+ );
+
+ const nestedSlots = $('article').eq(1);
+ expect(nestedSlots.find('> .fragmentSlot').html()).to.contain('1:');
+ expect(nestedSlots.find('> .pSlot > p').text()).to.contain('2:');
+ expect(nestedSlots.find('> .defaultSlot > article').text().replace(/\s+/g, ' ')).to.equal(
+ `
+ 3: nested fragmentSlot
+ 4: nested pSlot
+ 5: nested text in default slot
+ `.replace(/\s+/g, ' ')
+ );
+
+ expect($('article').eq(3).text().replace(/[^❌]/g, '')).to.equal('❌❌❌');
+
+ expect($('article').eq(4).text().replace(/[^❌]/g, '')).to.equal('❌❌❌');
+ });
+
+ it('Generate the right props for the layout', async () => {
+ const html = await fixture.readFile('/layout-props/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('#title').text()).to.equal('Hello world!');
+ expect($('#url').text()).to.equal('/layout-props');
+ expect($('#file').text()).to.match(/.*\/layout-props.md$/);
+ });
+});
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/add-classes.mjs b/packages/markdown/component/test/fixtures/astro-markdown-plugins/add-classes.mjs
new file mode 100644
index 000000000..39acabd52
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/add-classes.mjs
@@ -0,0 +1,18 @@
+import { selectAll } from 'hast-util-select';
+
+export default (additions) => {
+ const adders = Object.entries(additions).map(adder);
+ return (node) => adders.forEach((a) => a(node));
+};
+
+const adder = ([selector, className]) => {
+ const writer = write(className);
+ return (node) => selectAll(selector, node).forEach(writer);
+};
+
+const write =
+ (className) =>
+ ({ properties }) => {
+ if (!properties.className) properties.className = className;
+ else properties.className += ` ${className}`;
+ };
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-plugins/astro.config.mjs
new file mode 100644
index 000000000..08916b1fe
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/astro.config.mjs
@@ -0,0 +1,7 @@
+import { defineConfig } from 'astro/config';
+import preact from '@astrojs/preact';
+
+// https://astro.build/config
+export default defineConfig({
+ integrations: [preact()],
+});
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/package.json b/packages/markdown/component/test/fixtures/astro-markdown-plugins/package.json
new file mode 100644
index 000000000..4babf437a
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@test/astro-markdown-component-plugins",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/preact": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*",
+ "astro": "workspace:*",
+ "hast-util-select": "^5.0.2",
+ "rehype-slug": "^5.0.1"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro
new file mode 100644
index 000000000..f3a019f58
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro
@@ -0,0 +1,10 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/astro.config.mjs
new file mode 100644
index 000000000..130596b0c
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/astro.config.mjs
@@ -0,0 +1,19 @@
+const riGrammar = JSON.parse(
+ String.raw`{"name":"rinfo","patterns":[{"include":"#lf-rinfo"}],"repository":{"lf-rinfo":{"patterns":[{"include":"#control"},{"include":"#operator"},{"include":"#strings"},{"include":"#number"},{"include":"#comment"},{"include":"#literal"}]},"control":{"patterns":[{"name":"keyword.control.ri","match":"\\b(si|mientras|repetir)\\b"},{"name":"keyword.other.ri","match":"\\b(programa|robots|areas|variables|comenzar|fin)\\b"},{"name":"support.function.other.ri","match":"\\b(tomarFlor|HayFlorEnLaBolsa|HayFlorEnLaEsquina|depositarFlor|HayPapelEnLaBolsa|HayPapelEnLaEsquina|tomarPapel|depositarPapel)\\b"}]},"operator":{"comment":"Captures operators and also puts them in different sub-groups that further describe them","patterns":[{"match":"\\+|-|\\*|/","name":"keyword.operator.arithmetic.ri"},{"match":"<|>|<=|>=|=|<>|!=","name":"keyword.operator.comparison.ri"},{"match":"\\b(Pos|Informar|Leer|Iniciar|AsignarArea|AreaC)\\b","name":"support.function.arithmetic.ri"},{"match":":=","name":"keyword.operator.assign.ri"},{"match":"(&|~)","name":"support.function.logical.ri"}]},"strings":{"name":"string.quoted.double.ri","beginCaptures":{"0":{"name":"string.quoted.double.begin.ri"}},"endCaptures":{"0":{"name":"string.quoted.double.end.ri"}},"begin":"\"","end":"\"","patterns":[{"name":"constant.character.escape.ri","match":"\\\\."}]},"comment":{"patterns":[{"name":"comment.block.ri","begin":"{","end":"}","patterns":[{"include":"#comment"}]}]},"literal":{"patterns":[{"name":"constant.language.ri","match":"\\b(verdadero|falso|boolean|numero)\\b"}]},"number":{"patterns":[{"comment":"Captures decimal numbers, with the negative sign being considered an operator","match":"(-)?(?:((?:\\b\\d+(?:\\.\\d*)?|\\.\\d+)(?:\\b|e-?\\d+\\b)%?)|(\\$[0-9]+\\b))","captures":{"1":{"name":"keyword.operator.arithmetic.ri"},"2":{"name":"constant.numeric.decimal.ri"},"3":{"name":"constant.numeric.hex.ri"}}}]}},"scopeName":"source.rinfo"}`
+);
+
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: {
+ langs: [
+ {
+ id: 'rinfo',
+ scopeName: 'source.rinfo',
+ grammar: riGrammar,
+ aliases: ['ri'],
+ },
+ ],
+ },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/package.json
new file mode 100644
index 000000000..ecf89dba4
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-langs",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro
new file mode 100644
index 000000000..d6740987d
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro
@@ -0,0 +1,31 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```rinfo
+ programa Rinfo
+ areas
+ ciuadad: AreaC(1,1,100,100)
+ robots
+ robot robot1
+ comenzar
+ Informar(PosAv, PosCa)
+ fin
+ variables
+ Rinfo: robot1
+ comenzar
+ AsignarArea(Rinfo, ciudad)
+ Iniciar(Rinfo, 1, 1)
+ fin
+ ```
+
+ ```unknown
+ This language does not exist
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/astro.config.mjs
new file mode 100644
index 000000000..acd4c5abc
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/astro.config.mjs
@@ -0,0 +1,5 @@
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/package.json
new file mode 100644
index 000000000..6b54139e4
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-normal",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro
new file mode 100644
index 000000000..890471468
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro
@@ -0,0 +1,18 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```
+ plaintext
+ ```
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/package.json
new file mode 100644
index 000000000..876b42a66
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-component-shiki",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/astro.config.mjs
new file mode 100644
index 000000000..fa208789e
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/astro.config.mjs
@@ -0,0 +1,10 @@
+const serendipity = JSON.parse(
+ String.raw`{"name":"Serendipity Morning","type":"light","colors":{"activityBar.activeBorder":"#4E5377","activityBar.background":"#FDFDFE","activityBar.dropBorder":"#D8DAE4","activityBar.foreground":"#4E5377","activityBar.inactiveForeground":"#5F6488","activityBarBadge.background":"#F19A8E","activityBarBadge.foreground":"#FDFDFE","badge.background":"#F19A8E","badge.foreground":"#FDFDFE","banner.background":"#F1F1F4","banner.foreground":"#4E5377","banner.iconForeground":"#5F6488","breadcrumb.activeSelectionForeground":"#F19A8E","breadcrumb.background":"#FDFDFE","breadcrumb.focusForeground":"#5F6488","breadcrumb.foreground":"#8388AD","breadcrumbPicker.background":"#F1F1F4","button.background":"#F19A8E","button.foreground":"#FDFDFE","button.hoverBackground":"#F19A8Ee6","button.secondaryBackground":"#F1F1F4","button.secondaryForeground":"#4E5377","button.secondaryHoverBackground":"#D8DAE4","charts.lines":"#5F6488","charts.foreground":"#4E5377","charts.blue":"#7397DE","charts.green":"#3788BE","charts.orange":"#F19A8E","charts.purple":"#77AAB3","charts.red":"#D26A5D","charts.yellow":"#886CDB","checkbox.background":"#F1F1F4","checkbox.border":"#6e6a8614","checkbox.foreground":"#4E5377","debugExceptionWidget.background":"#F1F1F4","debugExceptionWidget.border":"#6e6a8614","debugIcon.breakpointCurrentStackframeForeground":"#5F6488","debugIcon.breakpointDisabledForeground":"#5F6488","debugIcon.breakpointForeground":"#5F6488","debugIcon.breakpointStackframeForeground":"#5F6488","debugIcon.breakpointUnverifiedForeground":"#5F6488","debugIcon.continueForeground":"#5F6488","debugIcon.disconnectForeground":"#5F6488","debugIcon.pauseForeground":"#5F6488","debugIcon.restartForeground":"#5F6488","debugIcon.startForeground":"#5F6488","debugIcon.stepBackForeground":"#5F6488","debugIcon.stepIntoForeground":"#5F6488","debugIcon.stepOutForeground":"#5F6488","debugIcon.stepOverForeground":"#5F6488","debugIcon.stopForeground":"#D26A5D","debugToolBar.background":"#F1F1F4","debugToolBar.border":"#D8DAE4","descriptionForeground":"#5F6488","diffEditor.border":"#D8DAE4","diffEditor.diagonalFill":"#6e6a8626","diffEditor.insertedTextBackground":"#7397DE14","diffEditor.insertedTextBorder":"#7397DE80","diffEditor.removedTextBackground":"#D26A5D14","diffEditor.removedTextBorder":"#D26A5D80","dropdown.background":"#F1F1F4","dropdown.border":"#6e6a8614","dropdown.foreground":"#4E5377","dropdown.listBackground":"#F1F1F4","editor.background":"#FDFDFE","editor.findMatchBackground":"#6e6a8626","editor.findMatchHighlightBackground":"#6e6a8626","editor.findRangeHighlightBackground":"#6e6a8626","editor.findRangeHighlightBorder":"#0000","editor.focusedStackFrameHighlightBackground":"#6e6a8614","editor.foldBackground":"#F1F1F4","editor.foreground":"#4E5377","editor.hoverHighlightBackground":"#0000","editor.inactiveSelectionBackground":"#6e6a860d","editor.inlineValuesBackground":"#0000","editor.inlineValuesForeground":"#5F6488","editor.lineHighlightBackground":"#6e6a860d","editor.lineHighlightBorder":"#0000","editor.linkedEditingBackground":"#F1F1F4","editor.rangeHighlightBackground":"#6e6a860d","editor.rangeHighlightBorder":"#0000","editor.selectionBackground":"#6e6a8614","editor.selectionForeground":"#4E5377","editor.selectionHighlightBackground":"#6e6a8614","editor.selectionHighlightBorder":"#0000","editor.snippetFinalTabstopHighlightBackground":"#6e6a8614","editor.snippetFinalTabstopHighlightBorder":"#F1F1F4","editor.snippetTabstopHighlightBackground":"#6e6a8614","editor.snippetTabstopHighlightBorder":"#F1F1F4","editor.stackFrameHighlightBackground":"#6e6a8614","editor.symbolHighlightBackground":"#6e6a8614","editor.symbolHighlightBorder":"#0000","editor.wordHighlightBackground":"#6e6a8614","editor.wordHighlightBorder":"#0000","editor.wordHighlightStrongBackground":"#6e6a8614","editor.wordHighlightStrongBorder":"#6e6a8614","editorBracketHighlight.foreground1":"#D26A5D80","editorBracketHighlight.foreground2":"#3788BE80","editorBracketHighlight.foreground3":"#886CDB80","editorBracketHighlight.foreground4":"#7397DE80","editorBracketHighlight.foreground5":"#F19A8E80","editorBracketHighlight.foreground6":"#77AAB380","editorBracketMatch.background":"#0000","editorBracketMatch.border":"#5F6488","editorBracketPairGuide.activeBackground1":"#3788BE","editorBracketPairGuide.activeBackground2":"#F19A8E","editorBracketPairGuide.activeBackground3":"#77AAB3","editorBracketPairGuide.activeBackground4":"#7397DE","editorBracketPairGuide.activeBackground5":"#886CDB","editorBracketPairGuide.activeBackground6":"#D26A5D","editorBracketPairGuide.background1":"#3788BE80","editorBracketPairGuide.background2":"#F19A8E80","editorBracketPairGuide.background3":"#77AAB380","editorBracketPairGuide.background4":"#7397DE80","editorBracketPairGuide.background5":"#886CDB80","editorBracketPairGuide.background6":"#D26A5D80","editorCodeLens.foreground":"#F19A8E","editorCursor.background":"#4E5377","editorCursor.foreground":"#8388AD","editorError.border":"#0000","editorError.foreground":"#D26A5D","editorGhostText.foreground":"#5F6488","editorGroup.border":"#0000","editorGroup.dropBackground":"#F1F1F4","editorGroup.emptyBackground":"#0000","editorGroup.focusedEmptyBorder":"#0000","editorGroupHeader.noTabsBackground":"#0000","editorGroupHeader.tabsBackground":"#0000","editorGroupHeader.tabsBorder":"#0000","editorGutter.addedBackground":"#7397DE","editorGutter.background":"#0000","editorGutter.commentRangeForeground":"#5F6488","editorGutter.deletedBackground":"#D26A5D","editorGutter.foldingControlForeground":"#77AAB3","editorGutter.modifiedBackground":"#F19A8E","editorHint.border":"#0000","editorHint.foreground":"#5F6488","editorHoverWidget.background":"#F1F1F4","editorHoverWidget.border":"#8388AD80","editorHoverWidget.foreground":"#5F6488","editorHoverWidget.highlightForeground":"#4E5377","editorHoverWidget.statusBarBackground":"#0000","editorIndentGuide.activeBackground":"#8388AD","editorIndentGuide.background":"#6e6a8626","editorInfo.border":"#D8DAE4","editorInfo.foreground":"#7397DE","editorInlayHint.background":"#D8DAE4","editorInlayHint.foreground":"#5F6488","editorInlayHint.parameterBackground":"#D8DAE4","editorInlayHint.parameterForeground":"#77AAB3","editorInlayHint.typeBackground":"#D8DAE4","editorInlayHint.typeForeground":"#7397DE","editorLightBulb.foreground":"#3788BE","editorLightBulbAutoFix.foreground":"#F19A8E","editorLineNumber.activeForeground":"#4E5377","editorLineNumber.foreground":"#5F6488","editorLink.activeForeground":"#F19A8E","editorMarkerNavigation.background":"#F1F1F4","editorMarkerNavigationError.background":"#F1F1F4","editorMarkerNavigationInfo.background":"#F1F1F4","editorMarkerNavigationWarning.background":"#F1F1F4","editorOverviewRuler.addedForeground":"#7397DE80","editorOverviewRuler.background":"#FDFDFE","editorOverviewRuler.border":"#6e6a8626","editorOverviewRuler.bracketMatchForeground":"#5F6488","editorOverviewRuler.commonContentForeground":"#6e6a860d","editorOverviewRuler.currentContentForeground":"#6e6a8614","editorOverviewRuler.deletedForeground":"#D26A5D80","editorOverviewRuler.errorForeground":"#D26A5D80","editorOverviewRuler.findMatchForeground":"#6e6a8626","editorOverviewRuler.incomingContentForeground":"#77AAB380","editorOverviewRuler.infoForeground":"#7397DE80","editorOverviewRuler.modifiedForeground":"#F19A8E80","editorOverviewRuler.rangeHighlightForeground":"#6e6a8626","editorOverviewRuler.selectionHighlightForeground":"#6e6a8626","editorOverviewRuler.warningForeground":"#886CDB80","editorOverviewRuler.wordHighlightForeground":"#6e6a8614","editorOverviewRuler.wordHighlightStrongForeground":"#6e6a8626","editorPane.background":"#0000","editorRuler.foreground":"#6e6a8626","editorSuggestWidget.background":"#F1F1F4","editorSuggestWidget.border":"#0000","editorSuggestWidget.focusHighlightForeground":"#F19A8E","editorSuggestWidget.foreground":"#5F6488","editorSuggestWidget.highlightForeground":"#F19A8E","editorSuggestWidget.selectedBackground":"#6e6a8614","editorSuggestWidget.selectedForeground":"#4E5377","editorSuggestWidget.selectedIconForeground":"#4E5377","editorUnnecessaryCode.border":"#0000","editorUnnecessaryCode.opacity":"#00000080","editorWarning.border":"#0000","editorWarning.foreground":"#886CDB","editorWhitespace.foreground":"#8388AD","editorWidget.background":"#F1F1F4","editorWidget.border":"#D8DAE4","editorWidget.foreground":"#5F6488","editorWidget.resizeBorder":"#8388AD","errorForeground":"#D26A5D","extensionBadge.remoteBackground":"#77AAB3","extensionBadge.remoteForeground":"#FDFDFE","extensionButton.prominentBackground":"#F19A8E","extensionButton.prominentForeground":"#FDFDFE","extensionButton.prominentHoverBackground":"#F19A8Ee6","extensionIcon.starForeground":"#F19A8E","extensionIcon.verifiedForeground":"#77AAB3","focusBorder":"#6e6a8614","foreground":"#4E5377","gitDecoration.addedResourceForeground":"#7397DE","gitDecoration.conflictingResourceForeground":"#D26A5D","gitDecoration.deletedResourceForeground":"#5F6488","gitDecoration.ignoredResourceForeground":"#8388AD","gitDecoration.modifiedResourceForeground":"#F19A8E","gitDecoration.renamedResourceForeground":"#3788BE","gitDecoration.stageDeletedResourceForeground":"#D26A5D","gitDecoration.stageModifiedResourceForeground":"#77AAB3","gitDecoration.submoduleResourceForeground":"#886CDB","gitDecoration.untrackedResourceForeground":"#886CDB","icon.foreground":"#5F6488","input.background":"#D8DAE480","input.border":"#6e6a8614","input.foreground":"#4E5377","input.placeholderForeground":"#5F6488","inputOption.activeBackground":"#F19A8E","inputOption.activeBorder":"#0000","inputOption.activeForeground":"#FDFDFE","inputValidation.errorBackground":"#0000","inputValidation.errorBorder":"#0000","inputValidation.errorForeground":"#D26A5D","inputValidation.infoBackground":"#0000","inputValidation.infoBorder":"#0000","inputValidation.infoForeground":"#7397DE","inputValidation.warningBackground":"#0000","inputValidation.warningBorder":"#0000","inputValidation.warningForeground":"#7397DE80","keybindingLabel.background":"#D8DAE4","keybindingLabel.border":"#6e6a8626","keybindingLabel.bottomBorder":"#6e6a8626","keybindingLabel.foreground":"#77AAB3","keybindingTable.headerBackground":"#D8DAE4","keybindingTable.rowsBackground":"#F1F1F4","list.activeSelectionBackground":"#6e6a8614","list.activeSelectionForeground":"#4E5377","list.activeSelectionIconForeground":"#4E5377","list.deemphasizedForeground":"#5F6488","list.dropBackground":"#F1F1F4","list.errorForeground":"#D26A5D","list.filterMatchBackground":"#F1F1F4","list.filterMatchBorder":"#F19A8E","list.focusBackground":"#6e6a8626","list.focusForeground":"#4E5377","list.focusOutline":"#6e6a8614","list.highlightForeground":"#F19A8E","list.hoverBackground":"#6e6a860d","list.hoverForeground":"#4E5377","list.inactiveFocusBackground":"#6e6a860d","list.inactiveSelectionBackground":"#F1F1F4","list.inactiveSelectionForeground":"#4E5377","list.inactiveSelectionIconForeground":"#5F6488","list.invalidItemForeground":"#D26A5D","list.warningForeground":"#886CDB","listFilterWidget.background":"#F1F1F4","listFilterWidget.noMatchesOutline":"#D26A5D","listFilterWidget.outline":"#D8DAE4","menu.background":"#F1F1F4","menu.border":"#6e6a860d","menu.foreground":"#4E5377","menu.selectionBackground":"#6e6a8614","menu.selectionBorder":"#D8DAE4","menu.selectionForeground":"#4E5377","menu.separatorBackground":"#4E5377","menubar.selectionBackground":"#6e6a8614","menubar.selectionBorder":"#6e6a860d","menubar.selectionForeground":"#4E5377","merge.border":"#D8DAE4","merge.commonContentBackground":"#6e6a8614","merge.commonHeaderBackground":"#6e6a8614","merge.currentContentBackground":"#886CDB80","merge.currentHeaderBackground":"#886CDB80","merge.incomingContentBackground":"#7397DE80","merge.incomingHeaderBackground":"#7397DE80","minimap.background":"#F1F1F4","minimap.errorHighlight":"#D26A5D80","minimap.findMatchHighlight":"#6e6a8614","minimap.selectionHighlight":"#6e6a8614","minimap.warningHighlight":"#886CDB80","minimapGutter.addedBackground":"#7397DE","minimapGutter.deletedBackground":"#D26A5D","minimapGutter.modifiedBackground":"#F19A8E","minimapSlider.activeBackground":"#6e6a8626","minimapSlider.background":"#6e6a8614","minimapSlider.hoverBackground":"#6e6a8614","notificationCenter.border":"#6e6a8614","notificationCenterHeader.background":"#F1F1F4","notificationCenterHeader.foreground":"#5F6488","notificationLink.foreground":"#77AAB3","notifications.background":"#F1F1F4","notifications.border":"#6e6a8614","notifications.foreground":"#4E5377","notificationsErrorIcon.foreground":"#D26A5D","notificationsInfoIcon.foreground":"#7397DE","notificationsWarningIcon.foreground":"#886CDB","notificationToast.border":"#6e6a8614","panel.background":"#F1F1F4","panel.border":"#0000","panel.dropBorder":"#D8DAE4","panelInput.border":"#F1F1F4","panelSection.dropBackground":"#6e6a8614","panelSectionHeader.background":"#F1F1F4","panelSectionHeader.foreground":"#4E5377","panelTitle.activeBorder":"#6e6a8626","panelTitle.activeForeground":"#4E5377","panelTitle.inactiveForeground":"#5F6488","peekView.border":"#D8DAE4","peekViewEditor.background":"#F1F1F4","peekViewEditor.matchHighlightBackground":"#6e6a8626","peekViewResult.background":"#F1F1F4","peekViewResult.fileForeground":"#5F6488","peekViewResult.lineForeground":"#5F6488","peekViewResult.matchHighlightBackground":"#6e6a8626","peekViewResult.selectionBackground":"#6e6a8614","peekViewResult.selectionForeground":"#4E5377","peekViewTitle.background":"#D8DAE4","peekViewTitleDescription.foreground":"#5F6488","pickerGroup.border":"#6e6a8626","pickerGroup.foreground":"#77AAB3","ports.iconRunningProcessForeground":"#F19A8E","problemsErrorIcon.foreground":"#D26A5D","problemsInfoIcon.foreground":"#7397DE","problemsWarningIcon.foreground":"#886CDB","progressBar.background":"#F19A8E","quickInput.background":"#F1F1F4","quickInput.foreground":"#5F6488","quickInputList.focusBackground":"#6e6a8614","quickInputList.focusForeground":"#4E5377","quickInputList.focusIconForeground":"#4E5377","scrollbar.shadow":"#0000","scrollbarSlider.activeBackground":"#6e6a8626","scrollbarSlider.background":"#6e6a860d","scrollbarSlider.hoverBackground":"#6e6a8614","searchEditor.findMatchBackground":"#6e6a8614","selection.background":"#6e6a8626","settings.focusedRowBackground":"#F1F1F4","settings.headerForeground":"#4E5377","settings.modifiedItemIndicator":"#F19A8E","settings.focusedRowBorder":"#6e6a8614","settings.rowHoverBackground":"#F1F1F4","sideBar.background":"#FDFDFE","sideBar.dropBackground":"#F1F1F4","sideBar.foreground":"#5F6488","sideBarSectionHeader.background":"#0000","sideBarSectionHeader.border":"#6e6a8614","statusBar.background":"#FDFDFE","statusBar.debuggingBackground":"#77AAB3","statusBar.debuggingForeground":"#FDFDFE","statusBar.foreground":"#5F6488","statusBar.noFolderBackground":"#FDFDFE","statusBar.noFolderForeground":"#5F6488","statusBarItem.activeBackground":"#6e6a8626","statusBarItem.hoverBackground":"#6e6a8614","statusBarItem.prominentBackground":"#D8DAE4","statusBarItem.prominentForeground":"#4E5377","statusBarItem.prominentHoverBackground":"#6e6a8614","statusBarItem.remoteBackground":"#FDFDFE","statusBarItem.remoteForeground":"#886CDB","statusBarItem.errorBackground":"#FDFDFE","statusBarItem.errorForeground":"#D26A5D","symbolIcon.arrayForeground":"#5F6488","symbolIcon.classForeground":"#5F6488","symbolIcon.colorForeground":"#5F6488","symbolIcon.constantForeground":"#5F6488","symbolIcon.constructorForeground":"#5F6488","symbolIcon.enumeratorForeground":"#5F6488","symbolIcon.enumeratorMemberForeground":"#5F6488","symbolIcon.eventForeground":"#5F6488","symbolIcon.fieldForeground":"#5F6488","symbolIcon.fileForeground":"#5F6488","symbolIcon.folderForeground":"#5F6488","symbolIcon.functionForeground":"#5F6488","symbolIcon.interfaceForeground":"#5F6488","symbolIcon.keyForeground":"#5F6488","symbolIcon.keywordForeground":"#5F6488","symbolIcon.methodForeground":"#5F6488","symbolIcon.moduleForeground":"#5F6488","symbolIcon.namespaceForeground":"#5F6488","symbolIcon.nullForeground":"#5F6488","symbolIcon.numberForeground":"#5F6488","symbolIcon.objectForeground":"#5F6488","symbolIcon.operatorForeground":"#5F6488","symbolIcon.packageForeground":"#5F6488","symbolIcon.propertyForeground":"#5F6488","symbolIcon.referenceForeground":"#5F6488","symbolIcon.snippetForeground":"#5F6488","symbolIcon.stringForeground":"#5F6488","symbolIcon.structForeground":"#5F6488","symbolIcon.textForeground":"#5F6488","symbolIcon.typeParameterForeground":"#5F6488","symbolIcon.unitForeground":"#5F6488","symbolIcon.variableForeground":"#5F6488","tab.activeBackground":"#6e6a860d","tab.activeForeground":"#4E5377","tab.activeModifiedBorder":"#7397DE","tab.border":"#0000","tab.hoverBackground":"#6e6a8614","tab.inactiveBackground":"#0000","tab.inactiveForeground":"#5F6488","tab.inactiveModifiedBorder":"#7397DE80","tab.lastPinnedBorder":"#8388AD","tab.unfocusedActiveBackground":"#0000","tab.unfocusedHoverBackground":"#0000","tab.unfocusedInactiveBackground":"#0000","tab.unfocusedInactiveModifiedBorder":"#7397DE80","terminal.ansiWhite":"#4E5377","terminal.ansiBrightWhite":"#4E5377","terminal.ansiBlack":"#D8DAE4","terminal.ansiBrightBlack":"#5F6488","terminal.ansiBlue":"#7397DE","terminal.ansiBrightBlue":"#7397DE","terminal.ansiCyan":"#F19A8E","terminal.ansiBrightCyan":"#F19A8E","terminal.ansiGreen":"#3788BE","terminal.ansiBrightGreen":"#3788BE","terminal.ansiMagenta":"#77AAB3","terminal.ansiBrightMagenta":"#77AAB3","terminal.ansiRed":"#D26A5D","terminal.ansiBrightRed":"#D26A5D","terminal.ansiBrightYellow":"#886CDB","terminal.ansiYellow":"#886CDB","terminal.dropBackground":"#6e6a8614","terminal.foreground":"#4E5377","terminal.selectionBackground":"#6e6a8614","terminal.tab.activeBorder":"#4E5377","terminalCursor.background":"#4E5377","terminalCursor.foreground":"#8388AD","textBlockQuote.background":"#F1F1F4","textBlockQuote.border":"#6e6a8614","textCodeBlock.background":"#F1F1F4","textLink.activeForeground":"#77AAB3e6","textLink.foreground":"#77AAB3","textPreformat.foreground":"#886CDB","textSeparator.foreground":"#5F6488","titleBar.activeBackground":"#FDFDFE","titleBar.activeForeground":"#5F6488","titleBar.inactiveBackground":"#F1F1F4","titleBar.inactiveForeground":"#5F6488","toolbar.activeBackground":"#6e6a8626","toolbar.hoverBackground":"#6e6a8614","tree.indentGuidesStroke":"#5F6488","walkThrough.embeddedEditorBackground":"#FDFDFE","welcomePage.background":"#FDFDFE","welcomePage.buttonBackground":"#F1F1F4","welcomePage.buttonHoverBackground":"#D8DAE4","widget.shadow":"#D8DAE44d","window.activeBorder":"#F1F1F4","window.inactiveBorder":"#F1F1F4"},"tokenColors":[{"scope":["comment"],"settings":{"foreground":"#8388AD","fontStyle":"italic"}},{"scope":["constant"],"settings":{"foreground":"#3788BE"}},{"scope":["constant.numeric","constant.language","constant.charcter.escape"],"settings":{"foreground":"#F19A8E"}},{"scope":["entity.name"],"settings":{"foreground":"#F19A8E"}},{"scope":["entity.name.section","entity.name.tag","entity.name.namespace","entity.name.type"],"settings":{"foreground":"#7397DE"}},{"scope":["entity.other.attribute-name","entity.other.inherited-class"],"settings":{"foreground":"#77AAB3","fontStyle":"italic"}},{"scope":["invalid"],"settings":{"foreground":"#D26A5D"}},{"scope":["invalid.deprecated"],"settings":{"foreground":"#5F6488"}},{"scope":["keyword"],"settings":{"foreground":"#3788BE"}},{"scope":["meta.tag","meta.brace"],"settings":{"foreground":"#4E5377"}},{"scope":["meta.import","meta.export"],"settings":{"foreground":"#3788BE"}},{"scope":"meta.directive.vue","settings":{"foreground":"#77AAB3","fontStyle":"italic"}},{"scope":"meta.property-name.css","settings":{"foreground":"#7397DE"}},{"scope":"meta.property-value.css","settings":{"foreground":"#886CDB"}},{"scope":"meta.tag.other.html","settings":{"foreground":"#5F6488"}},{"scope":["punctuation"],"settings":{"foreground":"#5F6488"}},{"scope":["punctuation.accessor"],"settings":{"foreground":"#3788BE"}},{"scope":["punctuation.definition.string"],"settings":{"foreground":"#886CDB"}},{"scope":["punctuation.definition.tag"],"settings":{"foreground":"#8388AD"}},{"scope":["storage.type","storage.modifier"],"settings":{"foreground":"#3788BE"}},{"scope":["string"],"settings":{"foreground":"#886CDB"}},{"scope":["support"],"settings":{"foreground":"#7397DE"}},{"scope":["support.constant"],"settings":{"foreground":"#886CDB"}},{"scope":["support.function"],"settings":{"foreground":"#D26A5D","fontStyle":"italic"}},{"scope":["variable"],"settings":{"foreground":"#F19A8E","fontStyle":"italic"}},{"scope":["variable.other","variable.language","variable.function","variable.argument"],"settings":{"foreground":"#4E5377"}},{"scope":["variable.parameter"],"settings":{"foreground":"#77AAB3"}}]}`
+);
+
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: { theme: serendipity },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/package.json
new file mode 100644
index 000000000..fd2a6e55f
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-themes-custom",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro
new file mode 100644
index 000000000..ec99141e1
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro
@@ -0,0 +1,14 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/astro.config.mjs
new file mode 100644
index 000000000..7ddfeeb16
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/astro.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: { theme: 'github-light' },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/package.json
new file mode 100644
index 000000000..b4c652551
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-themes-integrated",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro
new file mode 100644
index 000000000..ec99141e1
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro
@@ -0,0 +1,14 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/index.md b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/index.md
new file mode 100644
index 000000000..a75170537
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/index.md
@@ -0,0 +1,24 @@
+---
+layout: ../layouts/content.astro
+---
+
+# Hello world
+
+```yaml
+apiVersion: v3
+kind: Pod
+metadata:
+ name: rss-site
+ labels:
+ app: web
+spec:
+ containers:
+ - name: front-end
+ image: nginx
+ ports:
+ - containerPort: 80
+ - name: rss-reader
+ image: nickchase/rss-php-nginx:v1
+ ports:
+ - containerPort: 88
+```
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/astro.config.mjs
new file mode 100644
index 000000000..e95742529
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/astro.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: { wrap: false },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/package.json
new file mode 100644
index 000000000..13fd7a90a
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-wrap-false",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro
new file mode 100644
index 000000000..890471468
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro
@@ -0,0 +1,18 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```
+ plaintext
+ ```
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/astro.config.mjs
new file mode 100644
index 000000000..b1fa06335
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/astro.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: { wrap: null },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/package.json
new file mode 100644
index 000000000..6e1b2e8e6
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-wrap-null",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro
new file mode 100644
index 000000000..890471468
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro
@@ -0,0 +1,18 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```
+ plaintext
+ ```
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/astro.config.mjs
new file mode 100644
index 000000000..0e95f569b
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/astro.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ markdown: {
+ syntaxHighlight: 'shiki',
+ shikiConfig: { wrap: true },
+ },
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/package.json b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/package.json
new file mode 100644
index 000000000..e51b0a746
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-markdown-skiki-wrap-true",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro
new file mode 100644
index 000000000..890471468
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro
@@ -0,0 +1,18 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+---
+
+<Layout>
+ <Markdown>
+ # Hello world
+
+ ```
+ plaintext
+ ```
+
+ ```js
+ console.log('JavaScript')
+ ```
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs
new file mode 100644
index 000000000..be33a26cc
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs
@@ -0,0 +1,9 @@
+import { defineConfig } from 'astro/config';
+import preact from '@astrojs/preact';
+import svelte from "@astrojs/svelte";
+
+// https://astro.build/config
+export default defineConfig({
+ integrations: [preact(), svelte()],
+ site: 'https://astro.build/',
+});
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/package.json b/packages/markdown/component/test/fixtures/astro-markdown/package.json
new file mode 100644
index 000000000..a5864786e
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@test/astro-markdown-component",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/preact": "workspace:*",
+ "@astrojs/svelte": "workspace:*",
+ "@astrojs/markdown-component": "workspace:*",
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/Counter.jsx b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Counter.jsx
new file mode 100644
index 000000000..a75f858b5
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Counter.jsx
@@ -0,0 +1,7 @@
+import { h } from 'preact';
+import { useState } from 'preact/hooks';
+
+export default function () {
+ const [count, setCount] = useState(0);
+ return <button onClick={() => setCount(count + 1)}>{count}</button>;
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/Example.jsx b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Example.jsx
new file mode 100644
index 000000000..e1f67ee50
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Example.jsx
@@ -0,0 +1,5 @@
+import { h } from 'preact';
+
+export default function () {
+ return <div id="test">Testing</div>;
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/Hello.jsx b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Hello.jsx
new file mode 100644
index 000000000..d30dec516
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/Hello.jsx
@@ -0,0 +1,5 @@
+import { h } from 'preact';
+
+export default function ({ name }) {
+ return <div id="test">Hello {name}</div>;
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/SlotComponent.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/components/SlotComponent.astro
new file mode 100644
index 000000000..f0aa9fc1c
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/SlotComponent.astro
@@ -0,0 +1,13 @@
+<article>
+ <section class="fragmentSlot">
+ <slot name="fragmentSlot">❌ Missing content for slot "fragmentSlot"</slot>
+ </section>
+
+ <section class="pSlot">
+ <slot name="pSlot">❌ Missing content for slot "pSlot"</slot>
+ </section>
+
+ <section class="defaultSlot">
+ <slot>❌ Missing content for default slot</slot>
+ </section>
+</article>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/SvelteButton.svelte b/packages/markdown/component/test/fixtures/astro-markdown/src/components/SvelteButton.svelte
new file mode 100644
index 000000000..74f3ff6a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/SvelteButton.svelte
@@ -0,0 +1,11 @@
+<script>
+ let cool = false
+</script>
+
+<button on:click={() => cool = true}>This is cool right? {cool}</button>
+
+<style>
+ button {
+ background: green;
+ }
+</style>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/TextBlock.jsx b/packages/markdown/component/test/fixtures/astro-markdown/src/components/TextBlock.jsx
new file mode 100644
index 000000000..d9ea2534f
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/TextBlock.jsx
@@ -0,0 +1,20 @@
+import { h } from 'preact';
+
+const TextBlock = ({
+ title,
+ children,
+ noPadding = false,
+}) => {
+ return (
+ <div
+ className={`${
+ noPadding ? "" : "md:px-2 lg:px-4"
+ } flex-1 prose prose-headings:font-grotesk`}
+ >
+ <h3>{title}</h3>
+ <p>{children}</p>
+ </div>
+ );
+};
+
+export default TextBlock;
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/components/index.js b/packages/markdown/component/test/fixtures/astro-markdown/src/components/index.js
new file mode 100644
index 000000000..e7cc94c58
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/components/index.js
@@ -0,0 +1,5 @@
+import Counter from './Counter';
+
+export default {
+ Counter
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/content/code-element.md b/packages/markdown/component/test/fixtures/astro-markdown/src/content/code-element.md
new file mode 100644
index 000000000..b091decc0
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/content/code-element.md
@@ -0,0 +1,3 @@
+This should have `nospace` around it.
+
+This should have <code class="custom-class">nospace</code> around it.
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/plain.md b/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/plain.md
new file mode 100644
index 000000000..d548b3356
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/plain.md
@@ -0,0 +1,6 @@
+---
+---
+
+## Plain jane
+
+I am plain markdown!
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/with-components.md b/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/with-components.md
new file mode 100644
index 000000000..dc6c23bf9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/imported-md/with-components.md
@@ -0,0 +1,17 @@
+---
+setup: |
+ import Counter from '../components/Counter.jsx'
+ import Hello from '../components/Hello.jsx'
+ import SvelteButton from '../components/SvelteButton.svelte'
+---
+
+## With components
+
+### Non-hydrated
+
+<Hello name="Astro Naut" />
+
+### Hydrated
+
+<Counter client:load />
+<SvelteButton client:load />
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/content.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/content.astro
new file mode 100644
index 000000000..925a243a9
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/content.astro
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <!-- Head Stuff -->
+ </head>
+ <body>
+ <div class="container">
+ <slot></slot>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/layout-props.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/layout-props.astro
new file mode 100644
index 000000000..a11abb8fb
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/layouts/layout-props.astro
@@ -0,0 +1,17 @@
+---
+ interface Props {
+ url: string;
+ file: string;
+ title: string;
+ }
+
+ const { title, url, file } = Astro.props.content as Props;
+---
+
+<html>
+ <body>
+ <div id="title">{title}</div>
+ <div id="url">{url}</div>
+ <div id="file">{file}</div>
+ </body>
+</html> \ No newline at end of file
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/braces.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/braces.astro
new file mode 100644
index 000000000..d92ce6bc3
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/braces.astro
@@ -0,0 +1,13 @@
+---
+import Markdown from '@astrojs/markdown-component';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
+---
+
+<Markdown is:raw>
+ ## Interesting Topic
+
+ `({})`
+ `{...props}`
+ `{/* JavaScript */}`
+</Markdown>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/children.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/children.md
new file mode 100644
index 000000000..a22ee5f96
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/children.md
@@ -0,0 +1,12 @@
+---
+setup: import TextBlock from '../components/TextBlock'
+---
+{/* https://github.com/withastro/astro/issues/3319 */}
+
+<TextBlock title="Hello world!" noPadding>
+ <ul class="not-prose">
+ <li>A</li>
+ <li>B</li>
+ <li>C</li>
+ </ul>
+</TextBlock>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/close.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/close.astro
new file mode 100644
index 000000000..034d9a315
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/close.astro
@@ -0,0 +1,12 @@
+---
+import Markdown from '@astrojs/markdown-component';
+const content = `Markdown *content* to render`;
+---
+
+<main>
+ <div id="target">
+ <Markdown content={content} />
+ <p>Some other stuff</p>
+ </div>
+ <p>Lastly...</p>
+</main>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-element.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-element.astro
new file mode 100644
index 000000000..43ca0bfc5
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-element.astro
@@ -0,0 +1,7 @@
+---
+const content = await Astro.glob('../content/*.md');
+---
+
+<div>
+ {content.map(({ Content }) => <Content />)}
+</div>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-in-md.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-in-md.md
new file mode 100644
index 000000000..52a799ab1
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code-in-md.md
@@ -0,0 +1,16 @@
+# Inline code blocks
+
+`<script>` tags in **Astro** components are now built,
+bundled and optimized by default.
+
+> Markdown formatting still works between tags in inline code blocks.
+
+We can also use closing `</script>` tags without any problems.
+
+# Fenced code blocks
+
+```html
+<body>
+ <div>This should also work without any problems.</div>
+</body>
+```
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro
new file mode 100644
index 000000000..c3f9af2d3
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro
@@ -0,0 +1,13 @@
+---
+import Markdown from '@astrojs/markdown-component';
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
+---
+
+<Markdown>
+ ## Interesting Topic
+
+ ```js
+ const thing = () => {};
+ ```
+</Markdown>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment-with-js.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment-with-js.md
new file mode 100644
index 000000000..374463d2d
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment-with-js.md
@@ -0,0 +1,23 @@
+<!--
+HTML comments with */ inside!
+-->
+
+<!--
+```js
+/**
+ * It even works inside nested fenced code blocks!
+ */
+function test() {
+ /* Yay */
+ return 'Nice!';
+}
+```
+-->
+
+```
+<!-- HTML comments in code fence -->
+```
+
+`<!-- HTML comments in code -->`
+
+# It still works!
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment.md
new file mode 100644
index 000000000..39a916351
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/comment.md
@@ -0,0 +1,2 @@
+<!-- HTML comments! -->
+# It works!
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro
new file mode 100644
index 000000000..37d3807e4
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro
@@ -0,0 +1,18 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+import Hello from '../components/Hello.jsx';
+import Counter from '../components/Counter.jsx';
+
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
+---
+
+<Layout>
+ <Markdown>
+ ## Interesting Topic
+
+ <Hello name={`world`} />
+ <Counter client:load />
+ </Markdown>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/content.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/content.astro
new file mode 100644
index 000000000..a90bd8c31
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/content.astro
@@ -0,0 +1,6 @@
+---
+import Markdown from '@astrojs/markdown-component';
+const content = '# Foo';
+---
+
+<Markdown content={content} />
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/dash.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/dash.md
new file mode 100644
index 000000000..269a774f5
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/dash.md
@@ -0,0 +1,14 @@
+---
+title: My Blog Post
+layout: ../layouts/content.astro
+---
+
+## Title
+
+Hello world
+
+With this in the body ---
+
+## Another
+
+more content \ No newline at end of file
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro
new file mode 100644
index 000000000..32117a505
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro
@@ -0,0 +1,29 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+import Hello from '../components/Hello.jsx';
+import Counter from '../components/Counter.jsx';
+
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
+---
+
+<div id="deep">
+ <section class="a">
+ <Markdown>
+ ## A
+ </Markdown>
+ </section>
+
+ <section class="b">
+ <Markdown>
+ ## B
+ </Markdown>
+ </section>
+
+ <section class="c">
+ <Markdown>
+ ## C
+ </Markdown>
+ </section>
+</div>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/empty-code.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/empty-code.md
new file mode 100644
index 000000000..93cb4eedb
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/empty-code.md
@@ -0,0 +1,20 @@
+---
+title: My Blog Post
+layout: ../layouts/content.astro
+---
+
+## Title
+
+Hello world
+
+With this in the body ---
+
+## Another
+
+more content
+
+```
+
+```
+
+<pre></pre> \ No newline at end of file
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/external.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/external.astro
new file mode 100644
index 000000000..cf6273bce
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/external.astro
@@ -0,0 +1,19 @@
+---
+import Markdown from '@astrojs/markdown-component';
+
+const outer = `# Outer`;
+const inner = `## Inner`;
+---
+
+<style>
+ #root { color: green; }
+</style>
+<div id="root">
+ <Markdown content={outer} />
+
+ <Markdown>
+ # Nested
+
+ <Markdown content={inner} />
+ </Markdown>
+</div>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro
new file mode 100644
index 000000000..97cd8f211
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro
@@ -0,0 +1,9 @@
+---
+import Layout from '../../layouts/content.astro'
+
+const posts = await Astro.glob('../../imported-md/*.md')
+---
+
+<Layout>
+ {posts.map(({ Content }) => <Content />)}
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/jsx-expressions.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/jsx-expressions.md
new file mode 100644
index 000000000..b87efbb2d
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/jsx-expressions.md
@@ -0,0 +1,13 @@
+---
+title: Blog Post with JSX expressions
+paragraph: JSX at the start of the line!
+list: ['test-1', 'test-2', 'test-3']
+---
+
+## {frontmatter.title}
+
+{frontmatter.paragraph}
+
+<ul>
+ {frontmatter.list.map(item => <li id={item}>{item}</li>)}
+</ul>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/layout-props.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/layout-props.md
new file mode 100644
index 000000000..0f87c1bd0
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/layout-props.md
@@ -0,0 +1,4 @@
+---
+title: 'Hello world!'
+layout: '../layouts/layout-props.astro'
+--- \ No newline at end of file
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/namespace.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/namespace.md
new file mode 100644
index 000000000..abbe26a3b
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/namespace.md
@@ -0,0 +1,7 @@
+---
+setup: import ns from '../components/index.js';
+---
+
+# Hello Namespace!
+
+<ns.Counter>Click me!</ns.Counter>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro
new file mode 100644
index 000000000..3795f78f7
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro
@@ -0,0 +1,32 @@
+---
+// Component imports and setup JavaScript go here!
+import Markdown from '@astrojs/markdown-component';
+const content = `
+- list 1
+ - list 2
+- list
+ - - list
+1. Hello
+ 1. Hello`
+---
+
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Welcome to Astro</title>
+ </head>
+
+ <body>
+ <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
+ <div id="target">
+ <Markdown>
+ - list
+ - nested list
+
+ 1. Hello
+ 1. nested hello
+ </Markdown>
+ </div>
+ </body>
+</html>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro
new file mode 100644
index 000000000..aca4e31d5
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro
@@ -0,0 +1,5 @@
+---
+import Markdown from '@astrojs/markdown-component';
+---
+
+<Markdown></Markdown>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/post.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/post.astro
new file mode 100644
index 000000000..2745edae1
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/post.astro
@@ -0,0 +1,16 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+import Example from '../components/Example.jsx';
+
+const title = 'My Blog Post';
+const description = 'This is a post about some stuff.';
+---
+
+<Markdown>
+ ## Interesting Topic
+
+ <div id="first">Some content</div>
+
+ <Example></Example>
+</Markdown>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/raw-content.json.js b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/raw-content.json.js
new file mode 100644
index 000000000..21be533e1
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/raw-content.json.js
@@ -0,0 +1,10 @@
+import { rawContent, compiledContent } from '../imported-md/with-components.md';
+
+export async function get() {
+ return {
+ body: JSON.stringify({
+ raw: rawContent(),
+ compiled: await compiledContent(),
+ }),
+ }
+}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
new file mode 100644
index 000000000..1ee0e357d
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/scopedStyles-code.astro
@@ -0,0 +1,21 @@
+---
+import Markdown from '@astrojs/markdown-component';
+import Layout from '../layouts/content.astro';
+
+---
+<style>
+ #root {
+ color: green;
+ }
+</style>
+<Layout>
+ <div id="root">
+ <Markdown>
+ ## Interesting Topic
+
+ ```js
+ const thing = () => {};
+ ```
+ </Markdown>
+ </div>
+</Layout>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/script.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/script.md
new file mode 100644
index 000000000..f2b8bca88
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/script.md
@@ -0,0 +1,7 @@
+# Test
+
+## Let's try a script...
+
+This should work!
+
+<script src="/src/scripts/test.js" />
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slots.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slots.md
new file mode 100644
index 000000000..c7e8367b8
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slots.md
@@ -0,0 +1,38 @@
+---
+layout: ../layouts/content.astro
+setup: import SlotComponent from '../components/SlotComponent.astro';
+---
+
+# Component with slot contents in children
+
+<SlotComponent>
+ <div>4: Div in default slot</div>
+ <Fragment slot="fragmentSlot">
+ <div>1: Div in fragmentSlot</div>
+ <p>2: Paragraph in fragmentSlot</p>
+ </Fragment>
+ <Fragment><p>5: Paragraph in fragment in default slot</p></Fragment>
+ 6: Regular text in default slot
+ <p slot="pSlot" title="hello">3: p with title as pSlot</p>
+</SlotComponent>
+
+# Component with nested component in children
+
+<SlotComponent>
+ <p slot="pSlot">2: pSlot</p>
+ <SlotComponent>
+ <p slot="pSlot">4: nested pSlot</p>
+ 5: nested text in default slot
+ <Fragment slot="fragmentSlot">3: nested fragmentSlot</Fragment>
+ </SlotComponent>
+ <Fragment slot="fragmentSlot">1: fragmentSlot</Fragment>
+</SlotComponent>
+
+# Missing content due to empty children
+
+<SlotComponent>
+</SlotComponent>
+
+# Missing content due to self-closing tag
+
+<SlotComponent/>
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slug.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slug.md
new file mode 100644
index 000000000..77599b347
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/slug.md
@@ -0,0 +1,7 @@
+---
+title: My Blog Post
+---
+
+# {frontmatter.title}
+
+Hello world
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/vite-env-vars.md b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/vite-env-vars.md
new file mode 100644
index 000000000..30a9ab177
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/vite-env-vars.md
@@ -0,0 +1,35 @@
+---
+title: Referencing Vite Env Vars like import.meta.env.SITE, import.meta.env.TITLE and import.meta.env
+layout: ../layouts/content.astro
+---
+
+## Referencing the full name of Vite env vars
+
+You can get the configured site URL with `import.meta.env.SITE`.
+
+The variable `import.meta.env.TITLE` is not configured.
+
+You can reference all env vars through `import.meta.env`.
+
+This should also work outside of code blocks:
+- import.meta.env.SITE
+- import.meta.env.TITLE
+- import.meta.env
+
+## Usage in fenced code blocks with syntax highlighting
+
+```js
+// src/pages/rss.xml.js
+import rss from '@astrojs/rss';
+
+export const get = () => rss({
+ // Use Vite env vars with import.meta.env
+ site: import.meta.env.SITE,
+ title: import.meta.env.TITLE,
+ items: import.meta.glob('./**/*.md'),
+});
+```
+
+## Usage in frontmatter
+
+> frontmatter.title: {frontmatter.title}
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/scripts/test.js b/packages/markdown/component/test/fixtures/astro-markdown/src/scripts/test.js
new file mode 100644
index 000000000..b179ee953
--- /dev/null
+++ b/packages/markdown/component/test/fixtures/astro-markdown/src/scripts/test.js
@@ -0,0 +1 @@
+console.log("Hello world");
diff --git a/packages/markdown/component/test/test-utils.js b/packages/markdown/component/test/test-utils.js
new file mode 100644
index 000000000..db2cc6b18
--- /dev/null
+++ b/packages/markdown/component/test/test-utils.js
@@ -0,0 +1,12 @@
+import { loadFixture as baseLoadFixture } from '../../../astro/test/test-utils.js';
+
+export {
+ fixLineEndings
+} from '../../../astro/test/test-utils.js';
+
+export function loadFixture(config) {
+ if(config?.root) {
+ config.root = new URL(config.root, import.meta.url);
+ }
+ return baseLoadFixture(config);
+}