@gusnips/vite 0.8.21 → 0.8.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/head.d.ts.map +1 -1
- package/dist/head.js +19 -8
- package/dist/head.js.map +1 -1
- package/dist/preset.d.ts.map +1 -1
- package/dist/preset.js +3 -1
- package/dist/preset.js.map +1 -1
- package/package.json +2 -2
- package/src/head.test.ts +25 -0
- package/src/head.ts +25 -9
- package/src/preset.test.ts +13 -0
- package/src/preset.ts +2 -1
package/dist/head.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAiBA,4FAA4F;AAC5F,eAAO,MAAM,UAAU,4BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAiBA,4FAA4F;AAC5F,eAAO,MAAM,UAAU,4BAA0B,CAAC;AA0FlD,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB;yBACqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;2DACuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;gDAC4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAuHjE;AAED,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,cAAmB,GAC1B,IAAI,CAyDN"}
|
package/dist/head.js
CHANGED
|
@@ -41,11 +41,18 @@ const metaRes = (selector) => metaResFor(escapeRegex(selector));
|
|
|
41
41
|
/**
|
|
42
42
|
* Rewrite the value the first matching pattern captures between `$1` and `$2`, or null when the
|
|
43
43
|
* template carries no such tag. Tag-agnostic on purpose — the canonical `<link>` uses it too.
|
|
44
|
+
*
|
|
45
|
+
* Every page text in this file goes into `replace` through a FUNCTION, never as the replacement
|
|
46
|
+
* string. A string is read for `$` patterns: `$$` becomes `$`, and `$'` pastes in the rest of the
|
|
47
|
+
* template. One adopter's docs showed a place priced `"$$"`; the file said `"$"`, and React threw
|
|
48
|
+
* on hydration because the page no longer matched its own render.
|
|
44
49
|
*/
|
|
45
50
|
function rewriteAttr(html, patterns, value) {
|
|
46
51
|
for (const re of patterns) {
|
|
47
52
|
if (re.test(html))
|
|
48
|
-
return html.replace(re,
|
|
53
|
+
return html.replace(re, (_tag, before, after) => {
|
|
54
|
+
return before + escapeAttr(value) + after;
|
|
55
|
+
});
|
|
49
56
|
}
|
|
50
57
|
return null;
|
|
51
58
|
}
|
|
@@ -115,7 +122,7 @@ export function ogLocale(tag) {
|
|
|
115
122
|
export function bakeHead(template, tags) {
|
|
116
123
|
const shareTitle = tags.ogTitle ?? tags.title;
|
|
117
124
|
const shareDescription = tags.ogDescription ?? tags.description;
|
|
118
|
-
let html = template.replace(/<title>[^<]*<\/title>/, `<title>${escapeAttr(tags.title)}</title>`);
|
|
125
|
+
let html = template.replace(/<title>[^<]*<\/title>/, () => `<title>${escapeAttr(tags.title)}</title>`);
|
|
119
126
|
html = setMeta(html, 'name="description"', tags.description);
|
|
120
127
|
html = setMeta(html, 'property="og:title"', shareTitle);
|
|
121
128
|
html = setMeta(html, 'property="og:description"', shareDescription);
|
|
@@ -158,7 +165,7 @@ export function bakeHead(template, tags) {
|
|
|
158
165
|
const links = tags.alternates
|
|
159
166
|
.map((alt) => ` <link rel="alternate" hreflang="${escapeAttr(alt.hreflang)}" href="${escapeAttr(alt.href)}" />`)
|
|
160
167
|
.join("\n");
|
|
161
|
-
html = html.replace("</head>", `${links}\n </head>`);
|
|
168
|
+
html = html.replace("</head>", () => `${links}\n </head>`);
|
|
162
169
|
}
|
|
163
170
|
if (tags.lang !== undefined) {
|
|
164
171
|
// `og:locale` is not the same claim as `<html lang>` and is read by different machines.
|
|
@@ -173,7 +180,8 @@ export function bakeHead(template, tags) {
|
|
|
173
180
|
const alternates = (tags.alternates ?? [])
|
|
174
181
|
.filter((alt) => alt.hreflang !== "x-default" && alt.hreflang !== tags.lang)
|
|
175
182
|
.map((alt) => ` <meta property="og:locale:alternate" content="${escapeAttr(ogLocale(alt.hreflang))}" />`);
|
|
176
|
-
|
|
183
|
+
const locale = escapeAttr(ogLocale(tags.lang));
|
|
184
|
+
html = html.replace("</head>", () => ` <meta property="og:locale" content="${locale}" />\n${alternates.length ? `${alternates.join("\n")}\n` : ""} </head>`);
|
|
177
185
|
}
|
|
178
186
|
// A template that says `index, follow` gets its tag rewritten, not a second one beside it. A head
|
|
179
187
|
// carrying both leaves a contradiction to each crawler's own tie-break rule, and one adopter's
|
|
@@ -183,20 +191,23 @@ export function bakeHead(template, tags) {
|
|
|
183
191
|
rewriteAttr(html, metaRes('name="robots"'), "noindex") ??
|
|
184
192
|
html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
|
|
185
193
|
}
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
const { headExtra } = tags;
|
|
195
|
+
if (headExtra)
|
|
196
|
+
html = html.replace("</head>", () => ` ${headExtra}\n </head>`);
|
|
188
197
|
if (tags.lang !== undefined) {
|
|
189
198
|
const re = /(<html[^>]*\blang=")[^"]*(")/;
|
|
190
199
|
if (!re.test(html))
|
|
191
200
|
throw new Error("prerender: <html lang> not found in index.html");
|
|
192
|
-
|
|
201
|
+
const lang = escapeAttr(tags.lang);
|
|
202
|
+
html = html.replace(re, (_tag, before, after) => before + lang + after);
|
|
193
203
|
}
|
|
194
204
|
if (tags.body !== undefined) {
|
|
195
205
|
// Empty on purpose in the template, and it must STAY empty there: a second bake over an
|
|
196
206
|
// already-filled root would nest one render inside another.
|
|
197
207
|
if (!html.includes(EMPTY_ROOT))
|
|
198
208
|
throw new Error(`prerender: ${EMPTY_ROOT} not found in index.html`);
|
|
199
|
-
|
|
209
|
+
const { route, html: page } = tags.body;
|
|
210
|
+
html = html.replace(EMPTY_ROOT, () => `<div id="root" ${PRERENDERED_ROUTE_ATTR}="${escapeAttr(route)}">${page}</div>`);
|
|
200
211
|
}
|
|
201
212
|
return html;
|
|
202
213
|
}
|
package/dist/head.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,yFAAyF;AACzF,8FAA8F;AAC9F,yDAAyD;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,UAAU,GAAG,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,GAAG,CAAC,OAAe,EAAqB,EAAE,CAAC;IACzD,IAAI,MAAM,CAAC,cAAc,OAAO,yBAAyB,CAAC;IAC1D,IAAI,MAAM,CAAC,oCAAoC,OAAO,GAAG,CAAC;CAC3D,CAAC;AAEF,2FAA2F;AAC3F,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAqB,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE3F
|
|
1
|
+
{"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"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"}
|
package/dist/preset.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAE/C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAE/C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAWjF;AAED,MAAM,WAAW,gBAAgB;IAC/B;+DAC2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb;qCACiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;kCAC8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,QAAQ,GACT,EAAE,gBAAgB,GAAG,UAAU,CAa/B"}
|
package/dist/preset.js
CHANGED
|
@@ -22,7 +22,9 @@ export function htmlPlaceholders(values) {
|
|
|
22
22
|
return {
|
|
23
23
|
name: "frontkit:html-placeholders",
|
|
24
24
|
transformIndexHtml(html) {
|
|
25
|
-
return Object.entries(values).reduce(
|
|
25
|
+
return Object.entries(values).reduce(
|
|
26
|
+
// A function, because a replacement string reads `$$` as `$` (see `rewriteAttr`).
|
|
27
|
+
(out, [name, value]) => out.replaceAll(`%${name}%`, () => value), html);
|
|
26
28
|
},
|
|
27
29
|
};
|
|
28
30
|
}
|
package/dist/preset.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,MAAM,sBAAsB,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAwC;IACvE,OAAO;QACL,IAAI,EAAE,4BAA4B;QAClC,kBAAkB,CAAC,IAAY;YAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,MAAM,sBAAsB,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAwC;IACvE,OAAO;QACL,IAAI,EAAE,4BAA4B;QAClC,kBAAkB,CAAC,IAAY;YAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM;YAClC,kFAAkF;YAClF,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,EAChE,IAAI,CACL,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AA0BD,MAAM,UAAU,SAAS,CAAC,EACxB,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,QAAQ,GACS;IACjB,MAAM,OAAO,GAAG,WAAW,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC9E,OAAO;QACL,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5F,OAAO,EAAE;YACP,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;SAC1C;QACD,GAAG,CAAC,QAAQ,IAAI;YACd,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;SAC1F,CAAC;QACF,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;KAC7D,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gusnips/vite",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.23",
|
|
4
4
|
"description": "The build-time half of a prerendered Vite SPA: head baking, sitemap, robots, the prerender driver, a shared vite preset, and the translation catalog check.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"release:major": "bun pm version major && bun publish --access public"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@gusnips/react": "0.9.
|
|
72
|
+
"@gusnips/react": "0.9.14"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"@tailwindcss/vite": "^4",
|
package/src/head.test.ts
CHANGED
|
@@ -273,6 +273,31 @@ describe("bakeHead", () => {
|
|
|
273
273
|
);
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
+
it("writes a page's dollar signs as they are, in every field that takes page text", () => {
|
|
277
|
+
// A replacement STRING reads `$$` as `$` and `$'` as the rest of the template. One adopter's
|
|
278
|
+
// docs priced a place `"$$"`, the file said `"$"`, and hydration threw.
|
|
279
|
+
const money = "$$ $& $' $` $1";
|
|
280
|
+
const html = bakeHead(TEMPLATE, {
|
|
281
|
+
title: money,
|
|
282
|
+
description: money,
|
|
283
|
+
canonical: `https://acme.com/${money}`,
|
|
284
|
+
image: `https://acme.com/${money}.png`,
|
|
285
|
+
lang: "pt-BR",
|
|
286
|
+
alternates: [{ hreflang: "en", href: `https://acme.com/en/${money}` }],
|
|
287
|
+
headExtra: `<script type="application/ld+json">{"priceRange":"${money}"}</script>`,
|
|
288
|
+
body: { route: "/", html: `<p>${money}</p>` },
|
|
289
|
+
});
|
|
290
|
+
const escaped = "$$ $& $' $` $1";
|
|
291
|
+
expect(html).toContain(`<title>${escaped}</title>`);
|
|
292
|
+
expect(html).toContain(`<meta name="description" content="${escaped}" />`);
|
|
293
|
+
expect(html).toContain(`<link rel="canonical" href="https://acme.com/${escaped}" />`);
|
|
294
|
+
expect(html).toContain(`content="https://acme.com/${escaped}.png"`);
|
|
295
|
+
expect(html).toContain(`href="https://acme.com/en/${escaped}"`);
|
|
296
|
+
expect(html).toContain(`{"priceRange":"${money}"}`);
|
|
297
|
+
expect(html).toContain(`<p>${money}</p></div>`);
|
|
298
|
+
expect(html.match(/<\/html>/g)).toHaveLength(1);
|
|
299
|
+
});
|
|
300
|
+
|
|
276
301
|
it("refuses to bake a page into a page", () => {
|
|
277
302
|
const once = bakeHead(TEMPLATE, { ...BASE, body: { route: "/", html: "<main>hi</main>" } });
|
|
278
303
|
expect(() =>
|
package/src/head.ts
CHANGED
|
@@ -45,10 +45,18 @@ const metaRes = (selector: string): readonly RegExp[] => metaResFor(escapeRegex(
|
|
|
45
45
|
/**
|
|
46
46
|
* Rewrite the value the first matching pattern captures between `$1` and `$2`, or null when the
|
|
47
47
|
* template carries no such tag. Tag-agnostic on purpose — the canonical `<link>` uses it too.
|
|
48
|
+
*
|
|
49
|
+
* Every page text in this file goes into `replace` through a FUNCTION, never as the replacement
|
|
50
|
+
* string. A string is read for `$` patterns: `$$` becomes `$`, and `$'` pastes in the rest of the
|
|
51
|
+
* template. One adopter's docs showed a place priced `"$$"`; the file said `"$"`, and React threw
|
|
52
|
+
* on hydration because the page no longer matched its own render.
|
|
48
53
|
*/
|
|
49
54
|
function rewriteAttr(html: string, patterns: readonly RegExp[], value: string): string | null {
|
|
50
55
|
for (const re of patterns) {
|
|
51
|
-
if (re.test(html))
|
|
56
|
+
if (re.test(html))
|
|
57
|
+
return html.replace(re, (_tag, before: string, after: string) => {
|
|
58
|
+
return before + escapeAttr(value) + after;
|
|
59
|
+
});
|
|
52
60
|
}
|
|
53
61
|
return null;
|
|
54
62
|
}
|
|
@@ -188,7 +196,10 @@ export function bakeHead(template: string, tags: HeadTags): string {
|
|
|
188
196
|
const shareTitle = tags.ogTitle ?? tags.title;
|
|
189
197
|
const shareDescription = tags.ogDescription ?? tags.description;
|
|
190
198
|
|
|
191
|
-
let html = template.replace(
|
|
199
|
+
let html = template.replace(
|
|
200
|
+
/<title>[^<]*<\/title>/,
|
|
201
|
+
() => `<title>${escapeAttr(tags.title)}</title>`,
|
|
202
|
+
);
|
|
192
203
|
html = setMeta(html, 'name="description"', tags.description);
|
|
193
204
|
html = setMeta(html, 'property="og:title"', shareTitle);
|
|
194
205
|
html = setMeta(html, 'property="og:description"', shareDescription);
|
|
@@ -240,7 +251,7 @@ export function bakeHead(template: string, tags: HeadTags): string {
|
|
|
240
251
|
` <link rel="alternate" hreflang="${escapeAttr(alt.hreflang)}" href="${escapeAttr(alt.href)}" />`,
|
|
241
252
|
)
|
|
242
253
|
.join("\n");
|
|
243
|
-
html = html.replace("</head>", `${links}\n </head>`);
|
|
254
|
+
html = html.replace("</head>", () => `${links}\n </head>`);
|
|
244
255
|
}
|
|
245
256
|
|
|
246
257
|
if (tags.lang !== undefined) {
|
|
@@ -259,11 +270,13 @@ export function bakeHead(template: string, tags: HeadTags): string {
|
|
|
259
270
|
(alt) =>
|
|
260
271
|
` <meta property="og:locale:alternate" content="${escapeAttr(ogLocale(alt.hreflang))}" />`,
|
|
261
272
|
);
|
|
273
|
+
const locale = escapeAttr(ogLocale(tags.lang));
|
|
262
274
|
html = html.replace(
|
|
263
275
|
"</head>",
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
276
|
+
() =>
|
|
277
|
+
` <meta property="og:locale" content="${locale}" />\n${
|
|
278
|
+
alternates.length ? `${alternates.join("\n")}\n` : ""
|
|
279
|
+
} </head>`,
|
|
267
280
|
);
|
|
268
281
|
}
|
|
269
282
|
|
|
@@ -275,12 +288,14 @@ export function bakeHead(template: string, tags: HeadTags): string {
|
|
|
275
288
|
rewriteAttr(html, metaRes('name="robots"'), "noindex") ??
|
|
276
289
|
html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
|
|
277
290
|
}
|
|
278
|
-
|
|
291
|
+
const { headExtra } = tags;
|
|
292
|
+
if (headExtra) html = html.replace("</head>", () => ` ${headExtra}\n </head>`);
|
|
279
293
|
|
|
280
294
|
if (tags.lang !== undefined) {
|
|
281
295
|
const re = /(<html[^>]*\blang=")[^"]*(")/;
|
|
282
296
|
if (!re.test(html)) throw new Error("prerender: <html lang> not found in index.html");
|
|
283
|
-
|
|
297
|
+
const lang = escapeAttr(tags.lang);
|
|
298
|
+
html = html.replace(re, (_tag, before: string, after: string) => before + lang + after);
|
|
284
299
|
}
|
|
285
300
|
|
|
286
301
|
if (tags.body !== undefined) {
|
|
@@ -288,9 +303,10 @@ export function bakeHead(template: string, tags: HeadTags): string {
|
|
|
288
303
|
// already-filled root would nest one render inside another.
|
|
289
304
|
if (!html.includes(EMPTY_ROOT))
|
|
290
305
|
throw new Error(`prerender: ${EMPTY_ROOT} not found in index.html`);
|
|
306
|
+
const { route, html: page } = tags.body;
|
|
291
307
|
html = html.replace(
|
|
292
308
|
EMPTY_ROOT,
|
|
293
|
-
`<div id="root" ${PRERENDERED_ROUTE_ATTR}="${escapeAttr(
|
|
309
|
+
() => `<div id="root" ${PRERENDERED_ROUTE_ATTR}="${escapeAttr(route)}">${page}</div>`,
|
|
294
310
|
);
|
|
295
311
|
}
|
|
296
312
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { htmlPlaceholders } from "./preset.ts";
|
|
3
|
+
|
|
4
|
+
describe("htmlPlaceholders", () => {
|
|
5
|
+
it("writes a value's dollar signs as they are", () => {
|
|
6
|
+
// Narrowed to the plain function it is: this plugin's hook reads the page and nothing else,
|
|
7
|
+
// and Vite's hook type is a union of that and an object form this plugin does not use.
|
|
8
|
+
const transform = htmlPlaceholders({ PRICE: "$$ $& $'" }).transformIndexHtml as (
|
|
9
|
+
html: string,
|
|
10
|
+
) => string;
|
|
11
|
+
expect(transform("<p>%PRICE%</p><p>%PRICE%</p>")).toBe("<p>$$ $& $'</p><p>$$ $& $'</p>");
|
|
12
|
+
});
|
|
13
|
+
});
|
package/src/preset.ts
CHANGED
|
@@ -25,7 +25,8 @@ export function htmlPlaceholders(values: Readonly<Record<string, string>>): Plug
|
|
|
25
25
|
name: "frontkit:html-placeholders",
|
|
26
26
|
transformIndexHtml(html: string) {
|
|
27
27
|
return Object.entries(values).reduce(
|
|
28
|
-
|
|
28
|
+
// A function, because a replacement string reads `$$` as `$` (see `rewriteAttr`).
|
|
29
|
+
(out, [name, value]) => out.replaceAll(`%${name}%`, () => value),
|
|
29
30
|
html,
|
|
30
31
|
);
|
|
31
32
|
},
|