Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 21x 21x 21x | // SPDX-License-Identifier: MIT
/**
* Generates a self-contained inline JavaScript string that prevents FOUC
* (Flash of Unstyled Content) by applying the saved theme before first paint.
*
* The returned string is a complete IIFE suitable for injection into a
* `<script>` tag in the document `<head>`. It has no module dependencies.
*
* @example
* // Astro frontmatter
* import { generateBlockingScript } from '@lgtm-hq/turbo-theme-selector';
* const blockingScript = generateBlockingScript();
*
* // Astro template
* <Fragment set:html={`<script>${blockingScript}</script>`} />
*/
import { DEFAULT_THEME, VALID_THEMES } from '@lgtm-hq/turbo-themes-core';
import { CSS_LINK_ID, STORAGE_KEY, LEGACY_STORAGE_KEYS } from './constants.js';
export interface BlockingScriptOptions {
/** Valid theme IDs to accept. Defaults to VALID_THEMES from core. */
validThemes?: readonly string[];
/** Fallback theme when no preference is stored. Defaults to DEFAULT_THEME from core. */
defaultTheme?: string;
/** localStorage key for the active theme. Defaults to STORAGE_KEY from core. */
storageKey?: string;
/** Legacy localStorage keys to migrate from. Defaults to LEGACY_STORAGE_KEYS from core. */
legacyKeys?: readonly string[];
}
/**
* Generates a self-contained inline IIFE that prevents FOUC. It:
* 1. Migrates legacy storage keys to the current `storageKey`
* 2. Reads and validates the stored theme against the allowlist
* 3. Sets `data-theme` and the `theme-{id}` class on `<html>`
* 4. Sets `window.__INITIAL_THEME__` so `initTheme` can short-circuit
* 5. Updates the existing CSS `<link>` href when the theme is non-default
*
* The body uses readable identifiers; the bundler/gzip handles size. The
* outer try/catch keeps the page from breaking when localStorage throws
* (e.g. private browsing).
*/
export function generateBlockingScript(options: BlockingScriptOptions = {}): string {
const {
validThemes = VALID_THEMES,
defaultTheme = DEFAULT_THEME,
storageKey = STORAGE_KEY,
legacyKeys = LEGACY_STORAGE_KEYS,
} = options;
const config = {
storageKey: JSON.stringify(storageKey),
defaultTheme: JSON.stringify(defaultTheme),
validThemes: JSON.stringify(validThemes),
legacyKeys: JSON.stringify(legacyKeys),
cssLinkId: JSON.stringify(CSS_LINK_ID),
};
return `(function () {
try {
var storageKey = ${config.storageKey};
var defaultTheme = ${config.defaultTheme};
var validThemes = ${config.validThemes};
var legacyKeys = ${config.legacyKeys};
var cssLinkId = ${config.cssLinkId};
for (var i = 0; i < legacyKeys.length; i++) {
var legacyValue = localStorage.getItem(legacyKeys[i]);
if (legacyValue && !localStorage.getItem(storageKey)) {
localStorage.setItem(storageKey, legacyValue);
localStorage.removeItem(legacyKeys[i]);
}
}
var theme = localStorage.getItem(storageKey) || defaultTheme;
if (validThemes.indexOf(theme) === -1) theme = defaultTheme;
var root = document.documentElement;
root.setAttribute('data-theme', theme);
var classes = root.classList;
for (var j = classes.length - 1; j >= 0; j--) {
if (classes[j].indexOf('theme-') === 0) classes.remove(classes[j]);
}
classes.add('theme-' + theme);
window.__INITIAL_THEME__ = theme;
var rawBase = root.getAttribute('data-baseurl') || '';
var baseUrl = '';
if (rawBase && rawBase.indexOf('//') !== 0) {
try {
var parsed = new URL(rawBase, location.href);
if (parsed.origin === location.origin) {
baseUrl = parsed.pathname.replace(/[/]+$/g, '');
if (baseUrl === '/') baseUrl = '';
}
} catch (urlErr) {
baseUrl = '';
}
}
var link = document.getElementById(cssLinkId);
if (link) link.href = baseUrl + '/assets/css/themes/turbo/' + theme + '.css';
} catch (e) {
console.warn('Unable to load saved theme:', e);
}
})();`;
}
|