apps/web/src/lib/cookie-consent/activate-deferred-scripts.ts

1import type { StoredConsent } from './consent-state'
2import { categoryAllowed } from './consent-state'
3
4/**
5 * Deferred tags (blocked until consent). Example:
6 *
7 * ```html
8 * <script
9 *   type="text/plain"
10 *   data-cookie-category="analytics"
11 *   src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"
12 * ></script>
13 * ```
14 *
15 * Inline: use a plain `script` with `type="text/plain"` and your inline JS as text.
16 *
17 * Categories: `essential` | `functional` | `analytics` | `marketing` (essential is always allowed).
18 *
19 * Turns `type="text/plain"` scripts with `data-cookie-category` into real scripts when allowed.
20 * Copy attributes (incl. `src`) from the placeholder; strip the placeholder after insertion.
21 */
22export function activateDeferredScripts(consent: StoredConsent | null): void {
23	if (typeof document === 'undefined') {
24		return
25	}
26
27	const placeholders = document.querySelectorAll<HTMLScriptElement>(
28		'script[type="text/plain"][data-cookie-category]',
29	)
30
31	for (const node of placeholders) {
32		const cat = node.getAttribute('data-cookie-category')
33		if (!categoryAllowed(cat, consent)) {
34			continue
35		}
36
37		const created = document.createElement('script')
38		for (const { name, value } of node.attributes) {
39			if (name === 'type') {
40				created.type = 'text/javascript'
41				continue
42			}
43			created.setAttribute(name, value)
44		}
45		created.text = node.textContent ?? ''
46
47		const parent = node.parentNode
48		if (parent) {
49			parent.insertBefore(created, node)
50			parent.removeChild(node)
51		}
52	}
53}
54