Skip to content

Commit bfa709c

Browse files
committed
fix: move theme override out of inline scripts
1 parent 0fee1c0 commit bfa709c

6 files changed

Lines changed: 66 additions & 86 deletions

File tree

defaults/public/v8s-theme.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
(() => {
2+
const theme = new URLSearchParams(window.location.search).get("theme");
3+
if (theme !== "light" && theme !== "dark") return;
4+
5+
document.documentElement.dataset.theme = theme;
6+
7+
const applyThemeImages = () => {
8+
document.querySelectorAll('picture source[media*="prefers-color-scheme"][srcset]').forEach((source) => {
9+
const image = source.parentElement?.querySelector("img");
10+
const candidate =
11+
theme === "dark"
12+
? source.getAttribute("srcset")?.split(",")[0]?.trim()?.split(/\s+/)[0]
13+
: image?.getAttribute("src");
14+
if (image && candidate) image.src = candidate;
15+
});
16+
};
17+
18+
if (document.readyState === "loading") {
19+
document.addEventListener("DOMContentLoaded", applyThemeImages, { once: true });
20+
} else {
21+
applyThemeImages();
22+
}
23+
})();

scripts/build-html-core.test.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,24 @@ import {
3636
);
3737

3838
assert(normalized.includes('href="/v8s-style.css?v=123"'));
39+
assert(normalized.includes('src="/v8s-theme.js?v=123"'));
3940
assert(normalized.includes('rel="icon"'));
4041
assert(normalized.includes('rel="apple-touch-icon"'));
41-
assert(normalized.includes("data-v8s-theme-override"));
42-
assert(normalized.indexOf("data-v8s-theme-override") < normalized.indexOf('rel="stylesheet"'));
42+
assert(!normalized.includes("data-v8s-theme-override"));
43+
assert(normalized.indexOf("/v8s-theme.js") < normalized.indexOf('rel="stylesheet"'));
4344
}
4445

4546
{
4647
const original =
4748
'<head><script data-v8s-theme-override></script><link rel="icon" href="/custom.svg"><link rel="apple-touch-icon" href="/custom.png"><link rel="stylesheet" href="/v8s-style.css"></head>';
4849
const normalized = normalizeHtmlHead(original, { assetVersion: "123" });
4950

50-
assert.equal((normalized.match(/data-v8s-theme-override/g) || []).length, 1);
51+
assert.equal((normalized.match(/\/v8s-theme\.js/g) || []).length, 1);
52+
assert(!normalized.includes("data-v8s-theme-override"));
5153
assert.equal((normalized.match(/rel="icon"/g) || []).length, 1);
5254
assert.equal((normalized.match(/rel="apple-touch-icon"/g) || []).length, 1);
5355
}
5456

55-
assert(THEME_OVERRIDE_SCRIPT.includes("applyThemeImages"));
57+
assert(THEME_OVERRIDE_SCRIPT.includes("/v8s-theme.js"));
5658

5759
console.log("build html core tests ok");

scripts/lib/build-core/html-core.mjs

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,11 @@
11
const DEFAULT_PUBLIC_ASSET_VERSION = "20260601";
22

3-
export const THEME_OVERRIDE_SCRIPT = ` <script data-v8s-theme-override>
4-
(() => {
5-
const theme = new URLSearchParams(window.location.search).get("theme");
6-
if (theme !== "light" && theme !== "dark") return;
7-
8-
document.documentElement.dataset.theme = theme;
9-
10-
const applyThemeImages = () => {
11-
document.querySelectorAll('picture source[media*="prefers-color-scheme"][srcset]').forEach((source) => {
12-
const image = source.parentElement?.querySelector("img");
13-
const candidate =
14-
theme === "dark"
15-
? source.getAttribute("srcset")?.split(",")[0]?.trim()?.split(/\\s+/)[0]
16-
: image?.getAttribute("src");
17-
if (image && candidate) image.src = candidate;
18-
});
19-
};
20-
21-
if (document.readyState === "loading") {
22-
document.addEventListener("DOMContentLoaded", applyThemeImages, { once: true });
23-
} else {
24-
applyThemeImages();
25-
}
26-
})();
27-
</script>`;
3+
export const THEME_OVERRIDE_SCRIPT = themeOverrideScript();
284

295
export function normalizeHtmlHead(html, options = {}) {
306
const assetVersion = options.assetVersion || DEFAULT_PUBLIC_ASSET_VERSION;
317
let normalized = normalizePublicAssetVersions(html, assetVersion);
8+
normalized = replaceInlineThemeOverride(normalized, assetVersion);
329

3310
if (!normalized.includes('rel="icon"')) {
3411
normalized = insertBeforeHeadClose(normalized, ' <link rel="icon" type="image/svg+xml" href="/favicon.svg">\n');
@@ -38,15 +15,28 @@ export function normalizeHtmlHead(html, options = {}) {
3815
normalized = insertBeforeHeadClose(normalized, ' <link rel="apple-touch-icon" href="/apple-touch-icon.png">\n');
3916
}
4017

41-
if (!normalized.includes("data-v8s-theme-override")) {
42-
normalized = insertBeforeFirstStylesheet(normalized, `${THEME_OVERRIDE_SCRIPT}\n`);
18+
if (!normalized.includes("/v8s-theme.js")) {
19+
normalized = insertBeforeFirstStylesheet(normalized, `${themeOverrideScript(assetVersion)}\n`);
4320
}
4421

4522
return normalized;
4623
}
4724

4825
export function normalizePublicAssetVersions(html, assetVersion = DEFAULT_PUBLIC_ASSET_VERSION) {
49-
return html.replace(/(href=["']\/v8s-style\.css)(?:\?v=\d+)?(["'])/g, `$1?v=${assetVersion}$2`);
26+
return html
27+
.replace(/(href=["']\/v8s-style\.css)(?:\?v=\d+)?(["'])/g, `$1?v=${assetVersion}$2`)
28+
.replace(/(src=["']\/v8s-theme\.js)(?:\?v=\d+)?(["'])/g, `$1?v=${assetVersion}$2`);
29+
}
30+
31+
export function themeOverrideScript(assetVersion = DEFAULT_PUBLIC_ASSET_VERSION) {
32+
return ` <script src="/v8s-theme.js?v=${assetVersion}"></script>`;
33+
}
34+
35+
function replaceInlineThemeOverride(html, assetVersion) {
36+
return html.replace(
37+
/\s*<script data-v8s-theme-override>[\s\S]*?<\/script>\n?/,
38+
`\n${themeOverrideScript(assetVersion)}\n`
39+
);
5040
}
5141

5242
export function insertBeforeHeadClose(html, insertion) {

scripts/lib/custom-public-maintenance.mjs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,9 @@ function normalizeHtmlHead(html) {
392392
);
393393
}
394394

395-
if (!normalized.includes("data-v8s-theme-override")) {
395+
normalized = replaceInlineThemeOverride(normalized);
396+
397+
if (!normalized.includes("/v8s-theme.js")) {
396398
normalized = insertBeforeFirstStylesheet(normalized, `${THEME_OVERRIDE_SCRIPT}\n`);
397399
}
398400

@@ -415,31 +417,11 @@ function insertBeforeFirstStylesheet(html, insertion) {
415417
return insertBeforeHeadClose(html, insertion);
416418
}
417419

418-
const THEME_OVERRIDE_SCRIPT = ` <script data-v8s-theme-override>
419-
(() => {
420-
const theme = new URLSearchParams(window.location.search).get("theme");
421-
if (theme !== "light" && theme !== "dark") return;
422-
423-
document.documentElement.dataset.theme = theme;
424-
425-
const applyThemeImages = () => {
426-
document.querySelectorAll('picture source[media*="prefers-color-scheme"][srcset]').forEach((source) => {
427-
const image = source.parentElement?.querySelector("img");
428-
const candidate =
429-
theme === "dark"
430-
? source.getAttribute("srcset")?.split(",")[0]?.trim()?.split(/\\s+/)[0]
431-
: image?.getAttribute("src");
432-
if (image && candidate) image.src = candidate;
433-
});
434-
};
435-
436-
if (document.readyState === "loading") {
437-
document.addEventListener("DOMContentLoaded", applyThemeImages, { once: true });
438-
} else {
439-
applyThemeImages();
440-
}
441-
})();
442-
</script>`;
420+
const THEME_OVERRIDE_SCRIPT = ` <script src="/v8s-theme.js?v=${PUBLIC_ASSET_VERSION}"></script>`;
421+
422+
function replaceInlineThemeOverride(html) {
423+
return html.replace(/\s*<script data-v8s-theme-override>[\s\S]*?<\/script>\n?/, `\n${THEME_OVERRIDE_SCRIPT}\n`);
424+
}
443425

444426
function applyBranding(html, context, language = "en") {
445427
const siteConfig = context.siteConfig || {};

scripts/maintenance.test.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ function runCommand(cwd, args) {
138138
indexHtml = indexHtml
139139
.replace(/\s*<link rel="icon"[^>]+>\n/, "\n")
140140
.replace(/\s*<link rel="apple-touch-icon"[^>]+>\n/, "\n")
141-
.replace(/\s*<script data-v8s-theme-override>[\s\S]*?<\/script>\n/, "\n");
141+
.replace(/\s*<script src="\/v8s-theme\.js[^>]+><\/script>\n/, "\n");
142142
fs.writeFileSync(indexPath, indexHtml);
143143

144144
const doctorJson = JSON.parse(run(fixture, ["scripts/doctor.mjs", "--json"]));
@@ -156,7 +156,8 @@ function runCommand(cwd, args) {
156156
const fixedHtml = fs.readFileSync(indexPath, "utf8");
157157
assert.match(fixedHtml, /rel="icon"/);
158158
assert.match(fixedHtml, /rel="apple-touch-icon"/);
159-
assert.match(fixedHtml, /data-v8s-theme-override/);
159+
assert.match(fixedHtml, /\/v8s-theme\.js/);
160+
assert.doesNotMatch(fixedHtml, /data-v8s-theme-override/);
160161
}
161162

162163
{

scripts/setup.mjs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,9 @@ function normalizeHtmlHead(html) {
473473
);
474474
}
475475

476-
if (!normalized.includes("data-v8s-theme-override")) {
476+
normalized = replaceInlineThemeOverride(normalized);
477+
478+
if (!normalized.includes("/v8s-theme.js")) {
477479
normalized = insertBeforeFirstStylesheet(normalized, `${THEME_OVERRIDE_SCRIPT}\n`);
478480
}
479481

@@ -496,31 +498,11 @@ function insertBeforeFirstStylesheet(html, insertion) {
496498
return insertBeforeHeadClose(html, insertion);
497499
}
498500

499-
const THEME_OVERRIDE_SCRIPT = ` <script data-v8s-theme-override>
500-
(() => {
501-
const theme = new URLSearchParams(window.location.search).get("theme");
502-
if (theme !== "light" && theme !== "dark") return;
503-
504-
document.documentElement.dataset.theme = theme;
505-
506-
const applyThemeImages = () => {
507-
document.querySelectorAll('picture source[media*="prefers-color-scheme"][srcset]').forEach((source) => {
508-
const image = source.parentElement?.querySelector("img");
509-
const candidate =
510-
theme === "dark"
511-
? source.getAttribute("srcset")?.split(",")[0]?.trim()?.split(/\\s+/)[0]
512-
: image?.getAttribute("src");
513-
if (image && candidate) image.src = candidate;
514-
});
515-
};
516-
517-
if (document.readyState === "loading") {
518-
document.addEventListener("DOMContentLoaded", applyThemeImages, { once: true });
519-
} else {
520-
applyThemeImages();
521-
}
522-
})();
523-
</script>`;
501+
const THEME_OVERRIDE_SCRIPT = ` <script src="/v8s-theme.js?v=${PUBLIC_ASSET_VERSION}"></script>`;
502+
503+
function replaceInlineThemeOverride(html) {
504+
return html.replace(/\s*<script data-v8s-theme-override>[\s\S]*?<\/script>\n?/, `\n${THEME_OVERRIDE_SCRIPT}\n`);
505+
}
524506

525507
function languageForPublicFile(filePath) {
526508
const [language] = path.relative(CUSTOM_PUBLIC_DIR, filePath).split(path.sep);

0 commit comments

Comments
 (0)