diff --git a/.changeset/beige-kings-listen.md b/.changeset/beige-kings-listen.md
new file mode 100644
index 000000000..49ca26919
--- /dev/null
+++ b/.changeset/beige-kings-listen.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/markdown-remark': patch
+---
+
+Fix bug where code blocks would not be escaped properly
diff --git a/.changeset/curvy-glasses-rest.md b/.changeset/curvy-glasses-rest.md
new file mode 100644
index 000000000..8d0ae9c1f
--- /dev/null
+++ b/.changeset/curvy-glasses-rest.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix error with Markdown content attribute parsing
diff --git a/.changeset/young-steaks-report.md b/.changeset/young-steaks-report.md
new file mode 100644
index 000000000..c2d65335a
--- /dev/null
+++ b/.changeset/young-steaks-report.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix bug with attribute serialization
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index ea4a50ed9..2a5543d49 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -208,6 +208,8 @@ export function createAstro(fileURLStr: string, site: string): TopLevelAstro {
};
}
+const toAttributeString = (value: any) => String(value).replace(/&/g, '&').replace(/"/g, '"')
+
// A helper used to turn expressions into attribute key/value
export function addAttribute(value: any, key: string) {
if (value == null || value === false) {
@@ -216,10 +218,10 @@ export function addAttribute(value: any, key: string) {
// support "class" from an expression passed into an element (#782)
if (key === 'class:list') {
- return ` ${key.slice(0, -5)}="${serializeListValue(value)}"`;
+ return ` ${key.slice(0, -5)}="${toAttributeString(serializeListValue(value))}"`;
}
- return ` ${key}="${value}"`;
+ return ` ${key}="${toAttributeString(value)}"`;
}
// Adds support for `
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index 82ebd4d43..927b5b9d2 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -40,10 +40,11 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
${layout ? `import Layout from '${layout}';` : ''}
${components ? `import * from '${components}';` : ''}
${setup}
+const $$content = ${JSON.stringify(content)}
---`;
// If the user imported "Layout", wrap the content in a Layout
if (/\bLayout\b/.test(prelude)) {
- astroResult = `${prelude}\n\n\n${astroResult}\n\n`;
+ astroResult = `${prelude}\n\n\n${astroResult}\n\n`;
} else {
astroResult = `${prelude}\n${astroResult}`;
}
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts
index 5c707a4db..8ea5263b8 100644
--- a/packages/markdown/remark/src/index.ts
+++ b/packages/markdown/remark/src/index.ts
@@ -7,6 +7,7 @@ import rehypeExpressions from './rehype-expressions.js';
import rehypeIslands from './rehype-islands.js';
import { remarkJsx, loadRemarkJsx } from './remark-jsx.js';
import rehypeJsx from './rehype-jsx.js';
+import rehypeEscape from './rehype-escape.js';
import remarkPrism from './remark-prism.js';
import remarkUnwrap from './remark-unwrap.js';
import { loadPlugins } from './load-plugins.js';
@@ -76,6 +77,7 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
.use(isMDX ? [rehypeJsx] : [])
.use(isMDX ? [rehypeExpressions] : [])
.use(isMDX ? [] : [rehypeRaw])
+ .use(isMDX ? [rehypeEscape] : [])
.use(rehypeIslands);
let result: string;
diff --git a/packages/markdown/remark/src/rehype-escape.ts b/packages/markdown/remark/src/rehype-escape.ts
new file mode 100644
index 000000000..e0094b463
--- /dev/null
+++ b/packages/markdown/remark/src/rehype-escape.ts
@@ -0,0 +1,12 @@
+import { SKIP, visit } from 'unist-util-visit';
+
+export default function rehypeEscape(): any {
+ return function (node: any): any {
+ return visit(node, 'element', (el) => {
+ if (el.tagName === 'code' || el.tagName === 'pre') {
+ el.properties['data-astro-raw'] = true;
+ }
+ return el;
+ });
+ };
+}