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