Skip to content

Commit 3bb10f2

Browse files
committed
feat: Replace existing HTML meta tags
1 parent 34efcd2 commit 3bb10f2

File tree

4 files changed

+52
-10
lines changed

4 files changed

+52
-10
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export async function prerender(data) {
7979
// Sets the title for the current page: `<title>My cool page</title>`
8080
title: 'My cool page',
8181

82-
// Sets any additional elements you want injected into the `<head>`:
82+
// Sets any additional elements you want injected into the `<head>`.
83+
// `type: 'meta'` elements will replace existing matching tags in the input HTML:
8384
// <link rel="stylesheet" href="foo.css">
8485
// <meta property="og:title" content="Social media title">
8586
elements: new Set([

src/plugins/prerender-plugin.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
464464
body = result;
465465
}
466466

467-
const htmlHead = htmlDoc.querySelector('head');
467+
const htmlHead = /** @type {HTMLHeadElement} */ (htmlDoc.querySelector('head'));
468468
if (htmlHead) {
469469
if (head.title) {
470470
const htmlTitle = htmlHead.querySelector('title');
@@ -481,13 +481,27 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
481481
}
482482

483483
if (head.elements) {
484-
// Inject HTML links at the end of <head> for any stylesheets injected during rendering of the page:
485-
htmlHead.insertAdjacentHTML(
486-
'beforeend',
487-
Array.from(
488-
new Set(Array.from(head.elements).map(serializeElement)),
489-
).join('\n'),
490-
);
484+
for (const element of head.elements) {
485+
const serialized = serializeElement(element);
486+
487+
if (element.type === 'meta') {
488+
const keys = Object.keys(element.props || {});
489+
if (keys.length) {
490+
// We're going to (somewhat naively) assume that the first prop is
491+
// a unique identifier for the tag
492+
const key = keys[0];
493+
const val = element.props[key];
494+
495+
const existing = htmlHead.querySelector(`meta[${key}="${val}"]`);
496+
if (existing) {
497+
existing.replaceWith(serialized);
498+
continue;
499+
}
500+
}
501+
}
502+
503+
htmlHead.insertAdjacentHTML('beforeend', serialized);
504+
}
491505
}
492506
}
493507

tests/fixtures/simple/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4-
<meta charset="UTF-8" />
4+
<meta charset="UTF-8">
5+
<meta name="description" content="A simple web page with a prerendered script.">
56
</head>
67
<body>
78
<script prerender type="module" src="/src/index.js"></script>

tests/prerender-api.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,30 @@ test('Should support `head.elements` property', async () => {
116116
assert.match(prerenderedHtml, '<meta property="og:title" content="Social media title">');
117117
});
118118

119+
test('Should support `head.elements` property replacing', async () => {
120+
await loadFixture('simple', env);
121+
await writeEntry(env.tmp.path, `
122+
export async function prerender() {
123+
return {
124+
html: '<h1>Hello, World!</h1>',
125+
head: {
126+
elements: new Set([
127+
{ type: 'meta', props: { name: 'description', content: 'Foo' } },
128+
]),
129+
},
130+
};
131+
}
132+
`);
133+
await viteBuild(env.tmp.path);
134+
135+
const prerenderedHtml = await getOutputFile(env.tmp.path, 'index.html');
136+
const matches = Array.from(
137+
prerenderedHtml.matchAll(/<meta name="description"/g),
138+
(m) => m[0]
139+
);
140+
assert.is(matches.length, 1);
141+
assert.match(prerenderedHtml, '<meta name="description" content="Foo">');
142+
assert.not.match(prerenderedHtml, '<meta name="description" content="A simple web page with a prerendered script.">');
143+
});
144+
119145
test.run();

0 commit comments

Comments
 (0)