@codecademy/tracking 1.0.36-alpha.f16a34b536.0 → 1.0.36-alpha.fa0ece7093.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/dist/integrations/gtm.d.ts +2 -2
- package/dist/integrations/gtm.js +3 -3
- package/dist/integrations/index.d.ts +3 -4
- package/dist/integrations/index.js +17 -6
- package/dist/integrations/partytown/config.d.ts +2 -0
- package/dist/integrations/partytown/config.js +42 -0
- package/dist/integrations/partytown/gtm.d.ts +1 -0
- package/dist/integrations/partytown/gtm.js +41 -0
- package/dist/integrations/partytown/index.d.ts +1 -0
- package/dist/integrations/partytown/index.js +15 -0
- package/package.json +5 -2
|
@@ -3,7 +3,7 @@ export type GTMSettings = {
|
|
|
3
3
|
environment: string;
|
|
4
4
|
scope: TrackingWindow;
|
|
5
5
|
optedOutExternalTracking?: boolean;
|
|
6
|
-
|
|
6
|
+
enablePartytown?: boolean;
|
|
7
7
|
};
|
|
8
8
|
export declare const OPT_OUT_DATALAYER_VAR = "user_opted_out_external_tracking";
|
|
9
|
-
export declare const initializeGTM: ({ scope, environment, optedOutExternalTracking,
|
|
9
|
+
export declare const initializeGTM: ({ scope, environment, optedOutExternalTracking, enablePartytown, }: GTMSettings) => void;
|
package/dist/integrations/gtm.js
CHANGED
|
@@ -3,7 +3,7 @@ export const initializeGTM = _ref => {
|
|
|
3
3
|
let scope = _ref.scope,
|
|
4
4
|
environment = _ref.environment,
|
|
5
5
|
optedOutExternalTracking = _ref.optedOutExternalTracking,
|
|
6
|
-
|
|
6
|
+
enablePartytown = _ref.enablePartytown;
|
|
7
7
|
scope.dataLayer ??= [];
|
|
8
8
|
scope.dataLayer.push({
|
|
9
9
|
'gtm.start': new Date().getTime(),
|
|
@@ -22,13 +22,13 @@ export const initializeGTM = _ref => {
|
|
|
22
22
|
}
|
|
23
23
|
const gtm = document.createElement('script');
|
|
24
24
|
gtm.src = `https://www.googletagmanager.com/gtm.js?id=GTM-KTLK85W${preview_env}`;
|
|
25
|
-
if (
|
|
25
|
+
if (enablePartytown) {
|
|
26
26
|
gtm.type = 'text/partytown';
|
|
27
27
|
} else {
|
|
28
28
|
gtm.async = true;
|
|
29
29
|
}
|
|
30
30
|
document.getElementsByTagName('head')[0].appendChild(gtm);
|
|
31
|
-
if (
|
|
31
|
+
if (enablePartytown) {
|
|
32
32
|
window.dispatchEvent(new CustomEvent('ptupdate'));
|
|
33
33
|
}
|
|
34
34
|
};
|
|
@@ -17,12 +17,11 @@ export type TrackingIntegrationsSettings = {
|
|
|
17
17
|
*/
|
|
18
18
|
oneTrustScript?: string;
|
|
19
19
|
/**
|
|
20
|
-
* Use
|
|
21
|
-
* next.config.js experimental: { nextScriptWorkers } must be set to true.
|
|
20
|
+
* Use Partytown to load 3rd party scripts in a worker.
|
|
22
21
|
*/
|
|
23
|
-
|
|
22
|
+
enablePartytown?: boolean;
|
|
24
23
|
};
|
|
25
24
|
/**
|
|
26
25
|
* @see README.md for details and usage.
|
|
27
26
|
*/
|
|
28
|
-
export declare const initializeTrackingIntegrations: ({ environment, scope, optedOutExternalTracking, oneTrustScript,
|
|
27
|
+
export declare const initializeTrackingIntegrations: ({ environment, scope, optedOutExternalTracking, oneTrustScript, enablePartytown, }: TrackingIntegrationsSettings) => Promise<void>;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { initializeGTM } from './gtm';
|
|
2
2
|
import { initializeOneTrust } from './onetrust';
|
|
3
|
+
import { initializePartytown } from './partytown';
|
|
4
|
+
let init = false;
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* @see README.md for details and usage.
|
|
5
8
|
*/
|
|
@@ -8,22 +11,30 @@ export const initializeTrackingIntegrations = async _ref => {
|
|
|
8
11
|
scope = _ref.scope,
|
|
9
12
|
optedOutExternalTracking = _ref.optedOutExternalTracking,
|
|
10
13
|
oneTrustScript = _ref.oneTrustScript,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
enablePartytown = _ref.enablePartytown;
|
|
15
|
+
if (init) {
|
|
16
|
+
return; // Prevent multiple initializations
|
|
17
|
+
}
|
|
18
|
+
init = true;
|
|
19
|
+
if (enablePartytown) {
|
|
20
|
+
initializePartytown();
|
|
21
|
+
} else {
|
|
22
|
+
// Wait to allow any other post-hydration logic to run first
|
|
23
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
24
|
+
}
|
|
14
25
|
|
|
15
|
-
//
|
|
26
|
+
// Load in OneTrust's banner and wait for its `OptanonWrapper` callback
|
|
16
27
|
await initializeOneTrust({
|
|
17
28
|
scope,
|
|
18
29
|
environment,
|
|
19
30
|
scriptId: oneTrustScript
|
|
20
31
|
});
|
|
21
32
|
|
|
22
|
-
//
|
|
33
|
+
// Load GTM
|
|
23
34
|
initializeGTM({
|
|
24
35
|
scope,
|
|
25
36
|
environment,
|
|
26
37
|
optedOutExternalTracking,
|
|
27
|
-
|
|
38
|
+
enablePartytown
|
|
28
39
|
});
|
|
29
40
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const partytownConfig = () => ({
|
|
2
|
+
forward: ['dataLayer.push',
|
|
3
|
+
// for GTM
|
|
4
|
+
'fbq',
|
|
5
|
+
// for Facebook Pixel
|
|
6
|
+
'_hsq.push' // for Hubspot
|
|
7
|
+
],
|
|
8
|
+
lib: '/partytown/',
|
|
9
|
+
loadScriptsOnMainThread: [/googleads/,
|
|
10
|
+
// Google Ads
|
|
11
|
+
/bing/,
|
|
12
|
+
// Bing UET
|
|
13
|
+
/pepperjam/,
|
|
14
|
+
// Pepperjam
|
|
15
|
+
/snap/,
|
|
16
|
+
// Snap Pixel
|
|
17
|
+
/lightboxcdn/ // Digioh
|
|
18
|
+
],
|
|
19
|
+
/*
|
|
20
|
+
* This function runs in a worker and cannot access vars that might seem to
|
|
21
|
+
* be in scope in this file.
|
|
22
|
+
*/
|
|
23
|
+
resolveUrl(url, location, type) {
|
|
24
|
+
/*
|
|
25
|
+
* Block Partytown from handling GTM iframe and return href to main thread.
|
|
26
|
+
* See gtm.ts for explanation.
|
|
27
|
+
*/
|
|
28
|
+
if (url.hostname === 'www.googletagmanager.com' && type === 'iframe') {
|
|
29
|
+
new BroadcastChannel('gtm-iframe').postMessage(url.href);
|
|
30
|
+
return new URL('', 'https:.');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Proxy Facebook Pixel requests to resolve CORS issues
|
|
35
|
+
* see https://partytown.builder.io/facebook-pixel#proxy-requests
|
|
36
|
+
*/
|
|
37
|
+
if (url.hostname === 'connect.facebook.net') {
|
|
38
|
+
return new URL(`partytown-fb${url.pathname}${url.search}`, location.origin);
|
|
39
|
+
}
|
|
40
|
+
return url;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createGTMIframeOnBroadcast(): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GTM tries to create its own service worker, and it tries to load the script
|
|
3
|
+
* for creating that worker in an iframe. When a script within Partytown tries
|
|
4
|
+
* to create an iframe, Partytown overrides this behavior by making a request
|
|
5
|
+
* for the iframe src using fetch from a worker. This is typically good because
|
|
6
|
+
* it keeps the request off the main thread. However, fetches made from within
|
|
7
|
+
* a worker are more strict about CORS than requests natively made by iframe
|
|
8
|
+
* elements in the DOM. Google does not include a CORS response header for this
|
|
9
|
+
* particular iframe src. A typical workaround for this is to use a reverse
|
|
10
|
+
* proxy (which is what we do for Facebook Pixel requests), but that doesn't
|
|
11
|
+
* work for this iframe because the script being loaded within depends the
|
|
12
|
+
* iframe having a certain origin.
|
|
13
|
+
*
|
|
14
|
+
* The alternate workaround used here is to effectively block Partytown when it
|
|
15
|
+
* tries to resolve the iframe url (see resolveUrl in config.ts) and to send
|
|
16
|
+
* that url back to the main thread via BroadcastChannel so that a regular
|
|
17
|
+
* iframe can be created, just as GTM would have done outside of Partytown.
|
|
18
|
+
* With this approach, we still get the benefit of having Partytown handle the
|
|
19
|
+
* bulk of requests related to GTM.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const hiddenIframeProps = {
|
|
23
|
+
height: '0',
|
|
24
|
+
width: '0',
|
|
25
|
+
style: 'display: none; visibility: hidden;'
|
|
26
|
+
};
|
|
27
|
+
export function createGTMIframeOnBroadcast() {
|
|
28
|
+
new BroadcastChannel('gtm-iframe').onmessage = _ref => {
|
|
29
|
+
let data = _ref.data;
|
|
30
|
+
// This outer/inner approach matches the pattern GTM would normally use.
|
|
31
|
+
|
|
32
|
+
const outerIframe = document.createElement('iframe');
|
|
33
|
+
Object.assign(outerIframe, hiddenIframeProps);
|
|
34
|
+
document.body.appendChild(outerIframe);
|
|
35
|
+
const outerIframeDoc = outerIframe.contentWindow.document;
|
|
36
|
+
const innerIframe = outerIframeDoc.createElement('iframe');
|
|
37
|
+
Object.assign(innerIframe, hiddenIframeProps);
|
|
38
|
+
innerIframe.src = data;
|
|
39
|
+
outerIframeDoc.body.appendChild(innerIframe);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initializePartytown(): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { partytownSnippet } from '@builder.io/partytown/integration';
|
|
2
|
+
import { partytownConfig } from './config';
|
|
3
|
+
import { createGTMIframeOnBroadcast } from './gtm';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Encapsulation is necessary to avoid collision of global vars that are
|
|
7
|
+
* aliased in minification.
|
|
8
|
+
*/
|
|
9
|
+
const encapsulate = js => `(() => {${js}})();`;
|
|
10
|
+
export function initializePartytown() {
|
|
11
|
+
createGTMIframeOnBroadcast();
|
|
12
|
+
const ptScript = document.createElement('script');
|
|
13
|
+
ptScript.innerHTML = encapsulate(partytownSnippet(partytownConfig()));
|
|
14
|
+
document.head.appendChild(ptScript);
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecademy/tracking",
|
|
3
3
|
"description": "Tracking library for Codecademy",
|
|
4
|
-
"version": "1.0.36-alpha.
|
|
4
|
+
"version": "1.0.36-alpha.fa0ece7093.0",
|
|
5
5
|
"author": "Codecademy Engineering <dev@codecademy.com>",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@builder.io/partytown": "^0.10.2"
|
|
8
|
+
},
|
|
6
9
|
"files": [
|
|
7
10
|
"dist/**"
|
|
8
11
|
],
|
|
@@ -13,5 +16,5 @@
|
|
|
13
16
|
"access": "public"
|
|
14
17
|
},
|
|
15
18
|
"repository": "git@github.com:codecademy-engineering/mono.git",
|
|
16
|
-
"gitHead": "
|
|
19
|
+
"gitHead": "5ffbb7aac08439aa2488aa0c29011e93ee49f7e3"
|
|
17
20
|
}
|