@decocms/blocks 7.46.1 → 7.48.0
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.
- package/package.json +1 -1
- package/src/sdk/analytics.ts +72 -2
package/package.json
CHANGED
package/src/sdk/analytics.ts
CHANGED
|
@@ -18,9 +18,15 @@ export function useSendEvent({ on, event }: DataEventParams) {
|
|
|
18
18
|
/**
|
|
19
19
|
* Inline script that observes data-event attributes and dispatches events.
|
|
20
20
|
* Inject once in the root layout via a <script> tag.
|
|
21
|
+
*
|
|
22
|
+
* Prerender-safe: while a Speculation Rules prerender is running the document
|
|
23
|
+
* is hidden and its IntersectionObserver could fire phantom `view` events, so
|
|
24
|
+
* all init is deferred until the page is actually activated. On a normal load
|
|
25
|
+
* `document.prerendering` is false and it runs immediately (no behavior change).
|
|
21
26
|
*/
|
|
22
27
|
export const ANALYTICS_SCRIPT = `
|
|
23
28
|
(function() {
|
|
29
|
+
function start() {
|
|
24
30
|
function dispatch(event) {
|
|
25
31
|
if (window.dataLayer) {
|
|
26
32
|
window.dataLayer.push({ event: event.name, ...event.params });
|
|
@@ -67,11 +73,75 @@ export const ANALYTICS_SCRIPT = `
|
|
|
67
73
|
} else {
|
|
68
74
|
setTimeout(function() { mo.observe(document.body, { childList: true, subtree: true }); }, 0);
|
|
69
75
|
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (document.prerendering) {
|
|
79
|
+
document.addEventListener('prerenderingchange', start, { once: true });
|
|
80
|
+
} else {
|
|
81
|
+
start();
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Dev-only guardrail for Speculation Rules. While a prerender is running (the
|
|
88
|
+
* hidden pre-navigation document), NOTHING analytics-related should hit the
|
|
89
|
+
* network — otherwise the pageview/event is counted for a navigation that may
|
|
90
|
+
* never happen. This script patches the common egress sinks (sendBeacon, fetch,
|
|
91
|
+
* injected tracker <script>/<img>) and `console.error`s the offending call so
|
|
92
|
+
* the dev knows exactly which loader still needs a `document.prerendering`
|
|
93
|
+
* guard before activating speculation rules on that site.
|
|
94
|
+
*
|
|
95
|
+
* Emit only in dev (see DecoRootLayout) — it self-disables outside prerender,
|
|
96
|
+
* so it is inert on real navigations.
|
|
97
|
+
*/
|
|
98
|
+
export const SPECULATION_DEV_WARN_SCRIPT = `
|
|
99
|
+
(function(){
|
|
100
|
+
if (!document.prerendering) return;
|
|
101
|
+
var TRACKERS = /googletagmanager\\.com|google-analytics\\.com|analytics\\.google|gtag\\/js|gtm\\.js|connect\\.facebook\\.net|analytics\\.tiktok|hotjar|clarity\\.ms|doubleclick|\\/sgtm|sgtm\\.|\\/collect(\\?|$)|\\/j\\.php|vwo|tr\\?id=/i;
|
|
102
|
+
function err(what, detail){
|
|
103
|
+
console.error(
|
|
104
|
+
'[deco][speculation] ' + what + ' ran during prerender and will double-count when the page activates. ' +
|
|
105
|
+
'Guard its loader with: if (document.prerendering) { document.addEventListener("prerenderingchange", init, { once: true }); } else { init(); }',
|
|
106
|
+
detail
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
var sb = navigator.sendBeacon;
|
|
110
|
+
if (sb) navigator.sendBeacon = function(url){ err('navigator.sendBeacon', url); return sb.apply(navigator, arguments); };
|
|
111
|
+
var of = window.fetch;
|
|
112
|
+
if (of) window.fetch = function(input){
|
|
113
|
+
var u = (input && input.url) || input;
|
|
114
|
+
if (TRACKERS.test(String(u))) err('fetch()', u);
|
|
115
|
+
return of.apply(window, arguments);
|
|
116
|
+
};
|
|
117
|
+
var mo = new MutationObserver(function(muts){
|
|
118
|
+
for (var i=0;i<muts.length;i++){
|
|
119
|
+
var nodes = muts[i].addedNodes || [];
|
|
120
|
+
for (var j=0;j<nodes.length;j++){
|
|
121
|
+
var n = nodes[j];
|
|
122
|
+
if (!n || !n.tagName) continue;
|
|
123
|
+
if (n.tagName === 'SCRIPT' && n.src && TRACKERS.test(n.src)) err('tracker <script> load', n.src);
|
|
124
|
+
else if (n.tagName === 'IMG' && n.src && TRACKERS.test(n.src)) err('tracker pixel <img>', n.src);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
mo.observe(document.documentElement, { childList: true, subtree: true });
|
|
129
|
+
document.addEventListener('prerenderingchange', function(){
|
|
130
|
+
mo.disconnect();
|
|
131
|
+
if (sb) navigator.sendBeacon = sb;
|
|
132
|
+
if (of) window.fetch = of;
|
|
133
|
+
}, { once: true });
|
|
70
134
|
})();
|
|
71
135
|
`;
|
|
72
136
|
|
|
73
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* Returns a GTM container snippet. Returns empty string if no containerId.
|
|
139
|
+
*
|
|
140
|
+
* Prerender-safe: container load is deferred until the page is activated so a
|
|
141
|
+
* Speculation Rules prerender that never activates can't fire GTM (pageview /
|
|
142
|
+
* tags) in the hidden document. No-op guard on normal loads.
|
|
143
|
+
*/
|
|
74
144
|
export function gtmScript(containerId?: string): string {
|
|
75
145
|
if (!containerId) return "";
|
|
76
|
-
return `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${containerId}');`;
|
|
146
|
+
return `(function(){function boot(){(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${containerId}');}if(document.prerendering){document.addEventListener('prerenderingchange',boot,{once:true});}else{boot();}})();`;
|
|
77
147
|
}
|