Skip to content

Commit eb87a23

Browse files
authored
Escape set:text values in Astro JSX (#17847)
* Align set:text rendering in Astro JSX * Add set:text rendering changeset
1 parent b441180 commit eb87a23

4 files changed

Lines changed: 36 additions & 2 deletions

File tree

.changeset/rare-gifts-show.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes `set:text` escaping in MDX script and style elements

packages/astro/src/jsx-runtime/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Fragment, markHTMLString, Renderer } from '../runtime/server/index.js';
1+
import { escapeHTML, Fragment, markHTMLString, Renderer } from '../runtime/server/index.js';
22

33
const AstroJSX = 'astro:jsx';
44
const Empty = Symbol('empty');
@@ -65,7 +65,8 @@ function transformSetDirectives(vnode: AstroVNode) {
6565
return;
6666
}
6767
if ('set:text' in vnode.props) {
68-
const children = vnode.props['set:text'];
68+
const value = vnode.props['set:text'];
69+
const children = typeof value === 'string' ? markHTMLString(escapeHTML(value)) : value;
6970
delete vnode.props['set:text'];
7071
Object.assign(vnode.props, { children });
7172
return;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const scriptText = '</script><p id="script-text-following">script text</p>';
2+
export const styleText = '</style><p id="style-text-following">style text</p>';
3+
4+
<script id="script-text" type="application/json" set:text={scriptText} />
5+
<style id="style-text" set:text={styleText} />

packages/integrations/mdx/test/mdx-basics.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,16 @@ describe('MDX basics (merged fixture)', () => {
267267
'style should not be html-escaped',
268268
);
269269
});
270+
271+
it('keeps set:text within script and style elements', async () => {
272+
const html = await fixture.readFile('/set-text/index.html');
273+
const { document } = parseHTML(html);
274+
275+
assert.match(document.getElementById('script-text')!.textContent, /^&lt;\/script&gt;/);
276+
assert.match(document.getElementById('style-text')!.textContent, /^&lt;\/style&gt;/);
277+
assert.equal(document.getElementById('script-text-following'), null);
278+
assert.equal(document.getElementById('style-text-following'), null);
279+
});
270280
});
271281
});
272282

@@ -455,6 +465,19 @@ describe('MDX basics (merged fixture)', () => {
455465
'style should not be html-escaped',
456466
);
457467
});
468+
469+
it('keeps set:text within script and style elements', async () => {
470+
const res = await fixture.fetch('/set-text');
471+
assert.equal(res.status, 200);
472+
473+
const html = await res.text();
474+
const { document } = parseHTML(html);
475+
476+
assert.match(document.getElementById('script-text')!.textContent, /^&lt;\/script&gt;/);
477+
assert.match(document.getElementById('style-text')!.textContent, /^&lt;\/style&gt;/);
478+
assert.equal(document.getElementById('script-text-following'), null);
479+
assert.equal(document.getElementById('style-text-following'), null);
480+
});
458481
});
459482
});
460483
});

0 commit comments

Comments
 (0)