@gusnips/vite 0.5.3 → 0.5.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAiBA,4FAA4F;AAC5F,eAAO,MAAM,UAAU,4BAA0B,CAAC;AAmDlD,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,CAmGjE;AAED,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,cAAmB,GAC1B,IAAI,CA2CN"}
1
+ {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"AAiBA,4FAA4F;AAC5F,eAAO,MAAM,UAAU,4BAA0B,CAAC;AAkFlD,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,CA+GjE;AAED,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,cAAmB,GAC1B,IAAI,CA2CN"}
package/dist/head.js CHANGED
@@ -16,18 +16,49 @@ import { PRERENDERED_ROUTE_ATTR } from "@gusnips/react/contract";
16
16
  import { escapeAttr, escapeRegex } from "./escape.js";
17
17
  /** The empty root a template must carry — `bakeHead` fills it, and refuses a filled one. */
18
18
  export const EMPTY_ROOT = '<div id="root"></div>';
19
- /** `[^>]*` on both sides tolerates the attributes being split across lines, as Vite emits them. */
20
- const metaRe = (selector) => new RegExp(`(<meta[^>]*${escapeRegex(selector)}[^>]*content=")[^"]*(")`);
19
+ /**
20
+ * One tag, in BOTH attribute orders.
21
+ *
22
+ * `[^>]*` on either side already tolerates the attributes being split across lines, as Vite
23
+ * emits them. What it cannot tolerate is `content` coming FIRST — `<meta content="…"
24
+ * name="description">` is equally valid HTML, hand-written heads are full of it, and a regex
25
+ * anchored on "selector, then content" simply does not match. That matters more here than
26
+ * anywhere else in this file, because {@link setMeta} THROWS on no match: a template spelling
27
+ * one required tag the other way round is a red build with a misleading message, on a head that
28
+ * is perfectly correct. The fifth adopter's rig carried the reversed form as a second pattern;
29
+ * this is that, kept.
30
+ *
31
+ * The capture groups differ between the two, which is why they are separate patterns and not
32
+ * one alternation: forward captures `(…content=")value(")`, reversed captures
33
+ * `(<meta…content=")value("…selector)`. `$1`/`$2` mean the right thing in both.
34
+ */
35
+ const metaResFor = (pattern) => [
36
+ new RegExp(`(<meta[^>]*${pattern}[^>]*content=")[^"]*(")`),
37
+ new RegExp(`(<meta[^>]*content=")[^"]*("[^>]*${pattern})`),
38
+ ];
39
+ /** …from a selector written literally, as every caller but {@link eitherRes} writes it. */
40
+ const metaRes = (selector) => metaResFor(escapeRegex(selector));
41
+ /**
42
+ * Rewrite the value the first matching pattern captures between `$1` and `$2`, or null when the
43
+ * template carries no such tag. Tag-agnostic on purpose — the canonical `<link>` uses it too.
44
+ */
45
+ function rewriteAttr(html, patterns, value) {
46
+ for (const re of patterns) {
47
+ if (re.test(html))
48
+ return html.replace(re, `$1${escapeAttr(value)}$2`);
49
+ }
50
+ return null;
51
+ }
21
52
  /**
22
53
  * Replace the `content` of one `<meta …>`. Throws when the tag is missing, so an edit to
23
54
  * `index.html` fails the build instead of silently shipping the front door's description to
24
55
  * every crawler.
25
56
  */
26
57
  function setMeta(html, selector, value) {
27
- const re = metaRe(selector);
28
- if (!re.test(html))
58
+ const out = rewriteAttr(html, metaRes(selector), value);
59
+ if (out === null)
29
60
  throw new Error(`prerender: <meta ${selector}> not found in index.html`);
30
- return html.replace(re, `$1${escapeAttr(value)}$2`);
61
+ return out;
31
62
  }
32
63
  /**
33
64
  * The same regex, for a key a template may spell with EITHER attribute.
@@ -44,7 +75,7 @@ function setMeta(html, selector, value) {
44
75
  * That is the exact failure this file exists to prevent, arriving through the package rather
45
76
  * than despite it.
46
77
  */
47
- const eitherRe = (key) => new RegExp(`(<meta[^>]*(?:name|property)="${escapeRegex(key)}"[^>]*content=")[^"]*(")`);
78
+ const eitherRes = (key) => metaResFor(`(?:name|property)="${escapeRegex(key)}"`);
48
79
  /**
49
80
  * Replace the `content` of a tag a template is allowed not to carry.
50
81
  *
@@ -55,10 +86,10 @@ const eitherRe = (key) => new RegExp(`(<meta[^>]*(?:name|property)="${escapeRege
55
86
  * is why they are written rather than ignored. Everything a crawler actually depends on goes
56
87
  * through `setMeta`.
57
88
  *
58
- * Takes the regex rather than the selector, so each caller picks how strict the spelling is.
89
+ * Takes the patterns rather than the selector, so each caller picks how strict the spelling is.
59
90
  */
60
- function setMetaIfPresent(html, re, value) {
61
- return re.test(html) ? html.replace(re, `$1${escapeAttr(value)}$2`) : html;
91
+ function setMetaIfPresent(html, patterns, value) {
92
+ return rewriteAttr(html, patterns, value) ?? html;
62
93
  }
63
94
  /**
64
95
  * A BCP-47 tag in Open Graph's spelling: underscore, and a TERRITORY it will not infer.
@@ -88,35 +119,40 @@ export function bakeHead(template, tags) {
88
119
  html = setMeta(html, 'name="description"', tags.description);
89
120
  html = setMeta(html, 'property="og:title"', shareTitle);
90
121
  html = setMeta(html, 'property="og:description"', shareDescription);
91
- html = setMetaIfPresent(html, metaRe('name="title"'), tags.title);
92
- html = setMetaIfPresent(html, eitherRe("twitter:title"), shareTitle);
93
- html = setMetaIfPresent(html, eitherRe("twitter:description"), shareDescription);
122
+ html = setMetaIfPresent(html, metaRes('name="title"'), tags.title);
123
+ html = setMetaIfPresent(html, eitherRes("twitter:title"), shareTitle);
124
+ html = setMetaIfPresent(html, eitherRes("twitter:description"), shareDescription);
94
125
  if (tags.image !== undefined) {
95
126
  html = setMeta(html, 'property="og:image"', tags.image);
96
- html = setMetaIfPresent(html, eitherRe("twitter:image"), tags.image);
127
+ html = setMetaIfPresent(html, eitherRes("twitter:image"), tags.image);
97
128
  // A card's description goes with the card. Left alone, every page's own card was described
98
129
  // with the front page's title — the stale value this function exists to prevent, on the one
99
130
  // tag written for somebody who cannot see the picture.
100
- html = setMetaIfPresent(html, eitherRe("og:image:alt"), shareTitle);
131
+ html = setMetaIfPresent(html, eitherRes("og:image:alt"), shareTitle);
101
132
  }
102
133
  if (tags.canonical === null) {
103
134
  // Strip rather than blank: an empty canonical is a claim about "" and an empty og:url is a
104
135
  // share card pointing at the origin root. The image is left alone — it is a picture, not a
105
136
  // claim about this address, and a dead link that still unfurls the brand card is fine.
106
137
  html = html
107
- .replace(/\s*<link rel="canonical"[^>]*>/, "")
138
+ .replace(/\s*<link[^>]*rel="canonical"[^>]*>/, "")
108
139
  .replace(/\s*<meta[^>]*(?:name|property)="(?:og|twitter):url"[^>]*>/g, "");
109
140
  }
110
141
  else {
111
142
  html = setMeta(html, 'property="og:url"', tags.canonical);
112
- html = setMetaIfPresent(html, eitherRe("twitter:url"), tags.canonical);
143
+ html = setMetaIfPresent(html, eitherRes("twitter:url"), tags.canonical);
113
144
  // Demanded, like every meta tag a crawler depends on. `String.replace` with no match is a
114
145
  // silent no-op, so a template that never had a canonical would get one on no page at all
115
146
  // and say nothing about it — the exact failure `setMeta` exists to turn into a red build.
116
- const canonicalRe = /(<link rel="canonical" href=")[^"]*(")/;
117
- if (!canonicalRe.test(html))
147
+ // Both attribute orders, for the reason `metaResFor` gives — and this is the branch where
148
+ // getting it wrong REFUSES a correct head rather than skipping it.
149
+ const written = rewriteAttr(html, [
150
+ /(<link[^>]*rel="canonical"[^>]*href=")[^"]*(")/,
151
+ /(<link[^>]*href=")[^"]*("[^>]*rel="canonical")/,
152
+ ], tags.canonical);
153
+ if (written === null)
118
154
  throw new Error('prerender: <link rel="canonical"> not found in index.html');
119
- html = html.replace(canonicalRe, `$1${escapeAttr(tags.canonical)}$2`);
155
+ html = written;
120
156
  }
121
157
  if (tags.alternates?.length) {
122
158
  const links = tags.alternates
@@ -129,7 +165,11 @@ export function bakeHead(template, tags) {
129
165
  // APPENDED rather than set, because a Vite template carries no such tag — but one that
130
166
  // does would end up with two, and a crawler reading the first would get the template's
131
167
  // language on every page. So drop whatever is there before writing this page's own.
132
- html = html.replace(/\s*<meta property="og:locale(?::alternate)?"[^>]*>/g, "");
168
+ // `[^>]*` before the attribute as well as after it: a template that writes `content` first
169
+ // would survive the strip and keep its own tag beside the appended one, and a crawler
170
+ // reading the first of two gets the template's language on every page — which is the exact
171
+ // outcome the strip is here to prevent.
172
+ html = html.replace(/\s*<meta[^>]*property="og:locale(?::alternate)?"[^>]*>/g, "");
133
173
  const alternates = (tags.alternates ?? [])
134
174
  .filter((alt) => alt.hreflang !== "x-default" && alt.hreflang !== tags.lang)
135
175
  .map((alt) => ` <meta property="og:locale:alternate" content="${escapeAttr(ogLocale(alt.hreflang))}" />`);
@@ -139,10 +179,9 @@ export function bakeHead(template, tags) {
139
179
  // carrying both leaves a contradiction to each crawler's own tie-break rule, and one adopter's
140
180
  // 404 shipped exactly that.
141
181
  if (tags.noindex) {
142
- const robots = metaRe('name="robots"');
143
- html = robots.test(html)
144
- ? html.replace(robots, "$1noindex$2")
145
- : html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
182
+ html =
183
+ rewriteAttr(html, metaRes('name="robots"'), "noindex") ??
184
+ html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
146
185
  }
147
186
  if (tags.headExtra)
148
187
  html = html.replace("</head>", ` ${tags.headExtra}\n </head>`);
package/dist/head.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"head.js","sourceRoot":"","sources":["../src/head.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@
16
16
  * sitemap all install exactly what they use.
17
17
  */
18
18
  export { resolveKey } from "./catalog.ts";
19
+ export { escapeAttr } from "./escape.ts";
19
20
  export { assertRendered, bakeHead, EMPTY_ROOT, ogLocale, type Alternate, type HeadTags, type RenderedChecks, } from "./head.ts";
20
21
  export { assertOgImages, loadRenderer, loadTemplate, writeDist, writeOgCards, type OgCard, type WriteOgCardsOptions, } from "./node.ts";
21
22
  export { describeOverflow, fitText, OG_CANVAS, type FitOptions, type FitResult, type OgOverflow, } from "./og.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,SAAS,CAAC;AAKjB,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,SAAS,CAAC;AAKjB,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -16,6 +16,11 @@
16
16
  * sitemap all install exactly what they use.
17
17
  */
18
18
  export { resolveKey } from "./catalog.js";
19
+ // `headExtra` takes raw markup, so the moment an adopter builds a tag out of its own copy it
20
+ // needs the same escaping every tag this package writes already gets. Exported rather than
21
+ // re-invented per app: an adopter's hand-rolled copy is one that can drift from ours, and the
22
+ // first one to need it was building a `<link title="…">` out of a translated page title.
23
+ export { escapeAttr } from "./escape.js";
19
24
  export { assertRendered, bakeHead, EMPTY_ROOT, ogLocale, } from "./head.js";
20
25
  export { assertOgImages, loadRenderer, loadTemplate, writeDist, writeOgCards, } from "./node.js";
21
26
  export { describeOverflow, fitText, OG_CANVAS, } from "./og.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,GAIT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,GAGb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,SAAS,GAIV,MAAM,SAAS,CAAC;AAMjB,OAAO,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,GAIX,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,6FAA6F;AAC7F,2FAA2F;AAC3F,8FAA8F;AAC9F,yFAAyF;AACzF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,GAIT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,GAGb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,SAAS,GAIV,MAAM,SAAS,CAAC;AAMjB,OAAO,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,GAIX,MAAM,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gusnips/vite",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "The build-time half of a prerendered Vite SPA: head baking, sitemap, robots, the prerender driver, and a shared vite preset.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/head.test.ts CHANGED
@@ -56,6 +56,22 @@ const LEAN_TEMPLATE = `<!doctype html>
56
56
  */
57
57
  const NAME_TWITTER_TEMPLATE = TEMPLATE.replace(/property="twitter:/g, 'name="twitter:');
58
58
 
59
+ /**
60
+ * The same head with `content` written FIRST, on every tag including the canonical.
61
+ *
62
+ * Equally valid HTML, ordinary in a hand-written template, and the fifth adopter's own rig
63
+ * carried a second pattern for exactly this. Here it is worse than the spelling above: the
64
+ * narrow regex did not merely skip these tags, it made `setMeta` THROW — so a correct head
65
+ * failed the build saying the tag was "not found in index.html".
66
+ */
67
+ const CONTENT_FIRST_TEMPLATE = TEMPLATE.replace(
68
+ /<meta ((?:name|property)="[^"]*") (content="[^"]*")/g,
69
+ "<meta $2 $1",
70
+ ).replace(
71
+ '<link rel="canonical" href="https://acme.com/" />',
72
+ '<link href="https://acme.com/" rel="canonical" />',
73
+ );
74
+
59
75
  const BASE = { title: "T", description: "D", canonical: "https://acme.com/pricing" };
60
76
 
61
77
  describe("bakeHead", () => {
@@ -114,6 +130,35 @@ describe("bakeHead", () => {
114
130
  expect(html).toContain('<meta name="twitter:description" content="D" />');
115
131
  });
116
132
 
133
+ // The other half of the same class, and the louder one: a required tag written the other way
134
+ // round was a red build on a head that is perfectly correct.
135
+ it("writes a head that spells the content attribute first", () => {
136
+ const html = bakeHead(CONTENT_FIRST_TEMPLATE, { ...BASE, ogTitle: "share" });
137
+ expect(html).toContain('<meta content="D" name="description" />');
138
+ expect(html).toContain('<meta content="share" property="og:title" />');
139
+ expect(html).toContain('<meta content="share" property="twitter:title" />');
140
+ expect(html).toContain('<link href="https://acme.com/pricing" rel="canonical" />');
141
+ });
142
+
143
+ it("strips a reversed canonical and share URL from a shell", () => {
144
+ const html = bakeHead(CONTENT_FIRST_TEMPLATE, { ...BASE, canonical: null });
145
+ expect(html).not.toContain('rel="canonical"');
146
+ expect(html).not.toContain("og:url");
147
+ expect(html).not.toContain('content=""');
148
+ });
149
+
150
+ // Strip-then-append, so a strip that misses leaves two tags — and a crawler reads the first,
151
+ // which is the template's language rather than this page's.
152
+ it("replaces a reversed og:locale instead of writing a second one", () => {
153
+ const withLocale = CONTENT_FIRST_TEMPLATE.replace(
154
+ "</head>",
155
+ ' <meta content="en_US" property="og:locale" />\n </head>',
156
+ );
157
+ const html = bakeHead(withLocale, { ...BASE, lang: "pt-BR" });
158
+ expect(html.match(/property="og:locale"/g)).toHaveLength(1);
159
+ expect(html).toContain('content="pt_BR"');
160
+ });
161
+
117
162
  it("strips a share URL spelled with either attribute", () => {
118
163
  const html = bakeHead(NAME_TWITTER_TEMPLATE, { ...BASE, canonical: null });
119
164
  expect(html).not.toContain("twitter:url");
package/src/head.ts CHANGED
@@ -18,9 +18,40 @@ import { escapeAttr, escapeRegex } from "./escape.ts";
18
18
  /** The empty root a template must carry — `bakeHead` fills it, and refuses a filled one. */
19
19
  export const EMPTY_ROOT = '<div id="root"></div>';
20
20
 
21
- /** `[^>]*` on both sides tolerates the attributes being split across lines, as Vite emits them. */
22
- const metaRe = (selector: string): RegExp =>
23
- new RegExp(`(<meta[^>]*${escapeRegex(selector)}[^>]*content=")[^"]*(")`);
21
+ /**
22
+ * One tag, in BOTH attribute orders.
23
+ *
24
+ * `[^>]*` on either side already tolerates the attributes being split across lines, as Vite
25
+ * emits them. What it cannot tolerate is `content` coming FIRST — `<meta content="…"
26
+ * name="description">` is equally valid HTML, hand-written heads are full of it, and a regex
27
+ * anchored on "selector, then content" simply does not match. That matters more here than
28
+ * anywhere else in this file, because {@link setMeta} THROWS on no match: a template spelling
29
+ * one required tag the other way round is a red build with a misleading message, on a head that
30
+ * is perfectly correct. The fifth adopter's rig carried the reversed form as a second pattern;
31
+ * this is that, kept.
32
+ *
33
+ * The capture groups differ between the two, which is why they are separate patterns and not
34
+ * one alternation: forward captures `(…content=")value(")`, reversed captures
35
+ * `(<meta…content=")value("…selector)`. `$1`/`$2` mean the right thing in both.
36
+ */
37
+ const metaResFor = (pattern: string): readonly RegExp[] => [
38
+ new RegExp(`(<meta[^>]*${pattern}[^>]*content=")[^"]*(")`),
39
+ new RegExp(`(<meta[^>]*content=")[^"]*("[^>]*${pattern})`),
40
+ ];
41
+
42
+ /** …from a selector written literally, as every caller but {@link eitherRes} writes it. */
43
+ const metaRes = (selector: string): readonly RegExp[] => metaResFor(escapeRegex(selector));
44
+
45
+ /**
46
+ * Rewrite the value the first matching pattern captures between `$1` and `$2`, or null when the
47
+ * template carries no such tag. Tag-agnostic on purpose — the canonical `<link>` uses it too.
48
+ */
49
+ function rewriteAttr(html: string, patterns: readonly RegExp[], value: string): string | null {
50
+ for (const re of patterns) {
51
+ if (re.test(html)) return html.replace(re, `$1${escapeAttr(value)}$2`);
52
+ }
53
+ return null;
54
+ }
24
55
 
25
56
  /**
26
57
  * Replace the `content` of one `<meta …>`. Throws when the tag is missing, so an edit to
@@ -28,9 +59,9 @@ const metaRe = (selector: string): RegExp =>
28
59
  * every crawler.
29
60
  */
30
61
  function setMeta(html: string, selector: string, value: string): string {
31
- const re = metaRe(selector);
32
- if (!re.test(html)) throw new Error(`prerender: <meta ${selector}> not found in index.html`);
33
- return html.replace(re, `$1${escapeAttr(value)}$2`);
62
+ const out = rewriteAttr(html, metaRes(selector), value);
63
+ if (out === null) throw new Error(`prerender: <meta ${selector}> not found in index.html`);
64
+ return out;
34
65
  }
35
66
 
36
67
  /**
@@ -48,8 +79,8 @@ function setMeta(html: string, selector: string, value: string): string {
48
79
  * That is the exact failure this file exists to prevent, arriving through the package rather
49
80
  * than despite it.
50
81
  */
51
- const eitherRe = (key: string): RegExp =>
52
- new RegExp(`(<meta[^>]*(?:name|property)="${escapeRegex(key)}"[^>]*content=")[^"]*(")`);
82
+ const eitherRes = (key: string): readonly RegExp[] =>
83
+ metaResFor(`(?:name|property)="${escapeRegex(key)}"`);
53
84
 
54
85
  /**
55
86
  * Replace the `content` of a tag a template is allowed not to carry.
@@ -61,10 +92,10 @@ const eitherRe = (key: string): RegExp =>
61
92
  * is why they are written rather than ignored. Everything a crawler actually depends on goes
62
93
  * through `setMeta`.
63
94
  *
64
- * Takes the regex rather than the selector, so each caller picks how strict the spelling is.
95
+ * Takes the patterns rather than the selector, so each caller picks how strict the spelling is.
65
96
  */
66
- function setMetaIfPresent(html: string, re: RegExp, value: string): string {
67
- return re.test(html) ? html.replace(re, `$1${escapeAttr(value)}$2`) : html;
97
+ function setMetaIfPresent(html: string, patterns: readonly RegExp[], value: string): string {
98
+ return rewriteAttr(html, patterns, value) ?? html;
68
99
  }
69
100
 
70
101
  /** One `<link rel="alternate" hreflang>` — this page's address in another language. */
@@ -161,17 +192,17 @@ export function bakeHead(template: string, tags: HeadTags): string {
161
192
  html = setMeta(html, 'name="description"', tags.description);
162
193
  html = setMeta(html, 'property="og:title"', shareTitle);
163
194
  html = setMeta(html, 'property="og:description"', shareDescription);
164
- html = setMetaIfPresent(html, metaRe('name="title"'), tags.title);
165
- html = setMetaIfPresent(html, eitherRe("twitter:title"), shareTitle);
166
- html = setMetaIfPresent(html, eitherRe("twitter:description"), shareDescription);
195
+ html = setMetaIfPresent(html, metaRes('name="title"'), tags.title);
196
+ html = setMetaIfPresent(html, eitherRes("twitter:title"), shareTitle);
197
+ html = setMetaIfPresent(html, eitherRes("twitter:description"), shareDescription);
167
198
 
168
199
  if (tags.image !== undefined) {
169
200
  html = setMeta(html, 'property="og:image"', tags.image);
170
- html = setMetaIfPresent(html, eitherRe("twitter:image"), tags.image);
201
+ html = setMetaIfPresent(html, eitherRes("twitter:image"), tags.image);
171
202
  // A card's description goes with the card. Left alone, every page's own card was described
172
203
  // with the front page's title — the stale value this function exists to prevent, on the one
173
204
  // tag written for somebody who cannot see the picture.
174
- html = setMetaIfPresent(html, eitherRe("og:image:alt"), shareTitle);
205
+ html = setMetaIfPresent(html, eitherRes("og:image:alt"), shareTitle);
175
206
  }
176
207
 
177
208
  if (tags.canonical === null) {
@@ -179,18 +210,27 @@ export function bakeHead(template: string, tags: HeadTags): string {
179
210
  // share card pointing at the origin root. The image is left alone — it is a picture, not a
180
211
  // claim about this address, and a dead link that still unfurls the brand card is fine.
181
212
  html = html
182
- .replace(/\s*<link rel="canonical"[^>]*>/, "")
213
+ .replace(/\s*<link[^>]*rel="canonical"[^>]*>/, "")
183
214
  .replace(/\s*<meta[^>]*(?:name|property)="(?:og|twitter):url"[^>]*>/g, "");
184
215
  } else {
185
216
  html = setMeta(html, 'property="og:url"', tags.canonical);
186
- html = setMetaIfPresent(html, eitherRe("twitter:url"), tags.canonical);
217
+ html = setMetaIfPresent(html, eitherRes("twitter:url"), tags.canonical);
187
218
  // Demanded, like every meta tag a crawler depends on. `String.replace` with no match is a
188
219
  // silent no-op, so a template that never had a canonical would get one on no page at all
189
220
  // and say nothing about it — the exact failure `setMeta` exists to turn into a red build.
190
- const canonicalRe = /(<link rel="canonical" href=")[^"]*(")/;
191
- if (!canonicalRe.test(html))
221
+ // Both attribute orders, for the reason `metaResFor` gives — and this is the branch where
222
+ // getting it wrong REFUSES a correct head rather than skipping it.
223
+ const written = rewriteAttr(
224
+ html,
225
+ [
226
+ /(<link[^>]*rel="canonical"[^>]*href=")[^"]*(")/,
227
+ /(<link[^>]*href=")[^"]*("[^>]*rel="canonical")/,
228
+ ],
229
+ tags.canonical,
230
+ );
231
+ if (written === null)
192
232
  throw new Error('prerender: <link rel="canonical"> not found in index.html');
193
- html = html.replace(canonicalRe, `$1${escapeAttr(tags.canonical)}$2`);
233
+ html = written;
194
234
  }
195
235
 
196
236
  if (tags.alternates?.length) {
@@ -208,7 +248,11 @@ export function bakeHead(template: string, tags: HeadTags): string {
208
248
  // APPENDED rather than set, because a Vite template carries no such tag — but one that
209
249
  // does would end up with two, and a crawler reading the first would get the template's
210
250
  // language on every page. So drop whatever is there before writing this page's own.
211
- html = html.replace(/\s*<meta property="og:locale(?::alternate)?"[^>]*>/g, "");
251
+ // `[^>]*` before the attribute as well as after it: a template that writes `content` first
252
+ // would survive the strip and keep its own tag beside the appended one, and a crawler
253
+ // reading the first of two gets the template's language on every page — which is the exact
254
+ // outcome the strip is here to prevent.
255
+ html = html.replace(/\s*<meta[^>]*property="og:locale(?::alternate)?"[^>]*>/g, "");
212
256
  const alternates = (tags.alternates ?? [])
213
257
  .filter((alt) => alt.hreflang !== "x-default" && alt.hreflang !== tags.lang)
214
258
  .map(
@@ -227,10 +271,9 @@ export function bakeHead(template: string, tags: HeadTags): string {
227
271
  // carrying both leaves a contradiction to each crawler's own tie-break rule, and one adopter's
228
272
  // 404 shipped exactly that.
229
273
  if (tags.noindex) {
230
- const robots = metaRe('name="robots"');
231
- html = robots.test(html)
232
- ? html.replace(robots, "$1noindex$2")
233
- : html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
274
+ html =
275
+ rewriteAttr(html, metaRes('name="robots"'), "noindex") ??
276
+ html.replace("</head>", ` <meta name="robots" content="noindex" />\n </head>`);
234
277
  }
235
278
  if (tags.headExtra) html = html.replace("</head>", ` ${tags.headExtra}\n </head>`);
236
279
 
package/src/index.ts CHANGED
@@ -16,6 +16,11 @@
16
16
  * sitemap all install exactly what they use.
17
17
  */
18
18
  export { resolveKey } from "./catalog.ts";
19
+ // `headExtra` takes raw markup, so the moment an adopter builds a tag out of its own copy it
20
+ // needs the same escaping every tag this package writes already gets. Exported rather than
21
+ // re-invented per app: an adopter's hand-rolled copy is one that can drift from ours, and the
22
+ // first one to need it was building a `<link title="…">` out of a translated page title.
23
+ export { escapeAttr } from "./escape.ts";
19
24
  export {
20
25
  assertRendered,
21
26
  bakeHead,