@owlmeans/web-gtm 0.1.18-rc.2
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/build/.gitkeep +0 -0
- package/build/index.d.ts +30 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +52 -0
- package/build/index.js.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +65 -0
- package/tsconfig.json +11 -0
package/build/.gitkeep
ADDED
|
File without changes
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConsentOptions } from '@owlmeans/consent';
|
|
2
|
+
export interface GtmOptions extends ConsentOptions {
|
|
3
|
+
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** The queue name, when a page runs more than one container. */
|
|
6
|
+
dataLayerName?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The inline `<head>` snippet, consent first and the container second.
|
|
10
|
+
*
|
|
11
|
+
* The ORDER is the whole point of this package. Google Tag Manager decides what a tag may do from
|
|
12
|
+
* the consent state present when the container loads, and a React bundle cannot get there first —
|
|
13
|
+
* by the time an island mounts, the container has been running for hundreds of milliseconds. A
|
|
14
|
+
* site whose defaults arrive after `gtm.js` is not configured differently; it is unconfigured for
|
|
15
|
+
* the window that matters, and nothing in the page reports it.
|
|
16
|
+
*
|
|
17
|
+
* Emit this into the document head, above everything else, as an inline script.
|
|
18
|
+
*/
|
|
19
|
+
export declare const gtmHeadScript: (opts: GtmOptions) => string;
|
|
20
|
+
/** The `<noscript>` iframe, for the body. */
|
|
21
|
+
export declare const gtmNoscriptFrame: (opts: GtmOptions) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Load the container from script, for a host that cannot emit into its own head.
|
|
24
|
+
*
|
|
25
|
+
* The head snippet is strictly better and should be preferred; this exists for a single-page app
|
|
26
|
+
* whose HTML is not ours to edit. It still pushes the defaults first, and it still refuses to load
|
|
27
|
+
* a second time.
|
|
28
|
+
*/
|
|
29
|
+
export declare const loadGtm: (opts: GtmOptions) => void;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,SAAU,UAAU,KAAG,MAWhD,CAAA;AAED,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,SAAU,UAAU,KAAG,MAEsB,CAAA;AAE1E;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,SAAU,UAAU,KAAG,IAkB1C,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { consentBootstrapScript, consentStore } from '@owlmeans/consent';
|
|
2
|
+
/**
|
|
3
|
+
* The inline `<head>` snippet, consent first and the container second.
|
|
4
|
+
*
|
|
5
|
+
* The ORDER is the whole point of this package. Google Tag Manager decides what a tag may do from
|
|
6
|
+
* the consent state present when the container loads, and a React bundle cannot get there first —
|
|
7
|
+
* by the time an island mounts, the container has been running for hundreds of milliseconds. A
|
|
8
|
+
* site whose defaults arrive after `gtm.js` is not configured differently; it is unconfigured for
|
|
9
|
+
* the window that matters, and nothing in the page reports it.
|
|
10
|
+
*
|
|
11
|
+
* Emit this into the document head, above everything else, as an inline script.
|
|
12
|
+
*/
|
|
13
|
+
export const gtmHeadScript = (opts) => {
|
|
14
|
+
const layer = opts.dataLayerName ?? 'dataLayer';
|
|
15
|
+
return `${consentBootstrapScript(opts)};` +
|
|
16
|
+
`(function(w,d,s,l,i){w[l]=w[l]||[];` +
|
|
17
|
+
`w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
|
|
18
|
+
`var f=d.getElementsByTagName(s)[0],j=d.createElement(s),` +
|
|
19
|
+
`dl=l!='dataLayer'?'&l='+l:'';j.async=true;` +
|
|
20
|
+
`j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;` +
|
|
21
|
+
`f.parentNode.insertBefore(j,f)})` +
|
|
22
|
+
`(window,document,'script',${JSON.stringify(layer)},${JSON.stringify(opts.id)})`;
|
|
23
|
+
};
|
|
24
|
+
/** The `<noscript>` iframe, for the body. */
|
|
25
|
+
export const gtmNoscriptFrame = (opts) => `<iframe src="https://www.googletagmanager.com/ns.html?id=${encodeURIComponent(opts.id)}"` +
|
|
26
|
+
` height="0" width="0" style="display:none;visibility:hidden"></iframe>`;
|
|
27
|
+
/**
|
|
28
|
+
* Load the container from script, for a host that cannot emit into its own head.
|
|
29
|
+
*
|
|
30
|
+
* The head snippet is strictly better and should be preferred; this exists for a single-page app
|
|
31
|
+
* whose HTML is not ours to edit. It still pushes the defaults first, and it still refuses to load
|
|
32
|
+
* a second time.
|
|
33
|
+
*/
|
|
34
|
+
export const loadGtm = (opts) => {
|
|
35
|
+
if (typeof document === 'undefined') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const marker = `owl-gtm-${opts.id}`;
|
|
39
|
+
if (document.getElementById(marker) != null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Defaults before the container, every time — the same rule the head snippet exists to keep.
|
|
43
|
+
consentStore.init(opts);
|
|
44
|
+
const layer = opts.dataLayerName ?? 'dataLayer';
|
|
45
|
+
const script = document.createElement('script');
|
|
46
|
+
script.id = marker;
|
|
47
|
+
script.async = true;
|
|
48
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(opts.id)}`
|
|
49
|
+
+ (layer !== 'dataLayer' ? `&l=${encodeURIComponent(layer)}` : '');
|
|
50
|
+
document.head.appendChild(script);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAUxE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAgB,EAAU,EAAE;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,IAAI,WAAW,CAAA;IAE/C,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG;QACvC,qCAAqC;QACrC,+DAA+D;QAC/D,0DAA0D;QAC1D,4CAA4C;QAC5C,2DAA2D;QAC3D,kCAAkC;QAClC,6BAA6B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAA;AACpF,CAAC,CAAA;AAED,6CAA6C;AAC7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAgB,EAAU,EAAE,CAC3D,4DAA4D,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG;IAC1F,wEAAwE,CAAA;AAE1E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAgB,EAAQ,EAAE;IAChD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAM;IACR,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,EAAE,EAAE,CAAA;IACnC,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5C,OAAM;IACR,CAAC;IACD,6FAA6F;IAC7F,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEvB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,IAAI,WAAW,CAAA;IAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC/C,MAAM,CAAC,EAAE,GAAG,MAAM,CAAA;IAClB,MAAM,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,MAAM,CAAC,GAAG,GAAG,8CAA8C,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;UACpF,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACpE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;AACnC,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/web-gtm",
|
|
3
|
+
"version": "0.1.18-rc.2",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -b",
|
|
8
|
+
"dev": "sleep 2 && nodemon -e ts,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
9
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty",
|
|
10
|
+
"test": "bun test ./tests"
|
|
11
|
+
},
|
|
12
|
+
"main": "build/index.js",
|
|
13
|
+
"module": "build/index.js",
|
|
14
|
+
"types": "build/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./build/index.js",
|
|
18
|
+
"require": "./build/index.js",
|
|
19
|
+
"default": "./build/index.js",
|
|
20
|
+
"module": "./build/index.js",
|
|
21
|
+
"types": "./build/index.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@owlmeans/consent": "^0.1.18-rc.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
29
|
+
"@types/bun": "^1.4.0",
|
|
30
|
+
"nodemon": "^3.1.14",
|
|
31
|
+
"typescript": "^7.0.2"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { consentBootstrapScript, consentStore } from '@owlmeans/consent'
|
|
2
|
+
import type { ConsentOptions } from '@owlmeans/consent'
|
|
3
|
+
|
|
4
|
+
export interface GtmOptions extends ConsentOptions {
|
|
5
|
+
/** Container id, e.g. `GTM-XXXXXXX`. */
|
|
6
|
+
id: string
|
|
7
|
+
/** The queue name, when a page runs more than one container. */
|
|
8
|
+
dataLayerName?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The inline `<head>` snippet, consent first and the container second.
|
|
13
|
+
*
|
|
14
|
+
* The ORDER is the whole point of this package. Google Tag Manager decides what a tag may do from
|
|
15
|
+
* the consent state present when the container loads, and a React bundle cannot get there first —
|
|
16
|
+
* by the time an island mounts, the container has been running for hundreds of milliseconds. A
|
|
17
|
+
* site whose defaults arrive after `gtm.js` is not configured differently; it is unconfigured for
|
|
18
|
+
* the window that matters, and nothing in the page reports it.
|
|
19
|
+
*
|
|
20
|
+
* Emit this into the document head, above everything else, as an inline script.
|
|
21
|
+
*/
|
|
22
|
+
export const gtmHeadScript = (opts: GtmOptions): string => {
|
|
23
|
+
const layer = opts.dataLayerName ?? 'dataLayer'
|
|
24
|
+
|
|
25
|
+
return `${consentBootstrapScript(opts)};` +
|
|
26
|
+
`(function(w,d,s,l,i){w[l]=w[l]||[];` +
|
|
27
|
+
`w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
|
|
28
|
+
`var f=d.getElementsByTagName(s)[0],j=d.createElement(s),` +
|
|
29
|
+
`dl=l!='dataLayer'?'&l='+l:'';j.async=true;` +
|
|
30
|
+
`j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;` +
|
|
31
|
+
`f.parentNode.insertBefore(j,f)})` +
|
|
32
|
+
`(window,document,'script',${JSON.stringify(layer)},${JSON.stringify(opts.id)})`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The `<noscript>` iframe, for the body. */
|
|
36
|
+
export const gtmNoscriptFrame = (opts: GtmOptions): string =>
|
|
37
|
+
`<iframe src="https://www.googletagmanager.com/ns.html?id=${encodeURIComponent(opts.id)}"` +
|
|
38
|
+
` height="0" width="0" style="display:none;visibility:hidden"></iframe>`
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load the container from script, for a host that cannot emit into its own head.
|
|
42
|
+
*
|
|
43
|
+
* The head snippet is strictly better and should be preferred; this exists for a single-page app
|
|
44
|
+
* whose HTML is not ours to edit. It still pushes the defaults first, and it still refuses to load
|
|
45
|
+
* a second time.
|
|
46
|
+
*/
|
|
47
|
+
export const loadGtm = (opts: GtmOptions): void => {
|
|
48
|
+
if (typeof document === 'undefined') {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const marker = `owl-gtm-${opts.id}`
|
|
52
|
+
if (document.getElementById(marker) != null) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
// Defaults before the container, every time — the same rule the head snippet exists to keep.
|
|
56
|
+
consentStore.init(opts)
|
|
57
|
+
|
|
58
|
+
const layer = opts.dataLayerName ?? 'dataLayer'
|
|
59
|
+
const script = document.createElement('script')
|
|
60
|
+
script.id = marker
|
|
61
|
+
script.async = true
|
|
62
|
+
script.src = `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(opts.id)}`
|
|
63
|
+
+ (layer !== 'dataLayer' ? `&l=${encodeURIComponent(layer)}` : '')
|
|
64
|
+
document.head.appendChild(script)
|
|
65
|
+
}
|
package/tsconfig.json
ADDED