UNPKG

react-ga4

Version:
308 lines (307 loc) 10.4 kB
//#region src/gtag.ts const gtag = (...args) => { if (typeof window !== "undefined") { if (typeof window.gtag === "undefined") { window.dataLayer = window.dataLayer || []; window.gtag = function gtag() { window.dataLayer.push(arguments); }; } window.gtag(...args); } }; //#endregion //#region src/format.ts const smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i; function toTitleCase(string) { return string.toString().trim().replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, (match, index, title) => { if (index > 0 && index + match.length !== title.length && match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" && (title.charAt(index + match.length) !== "-" || title.charAt(index - 1) === "-") && title.charAt(index - 1).search(/[^\s-]/) < 0) return match.toLowerCase(); if (match.substr(1).search(/[A-Z]|\../) > -1) return match; return match.charAt(0).toUpperCase() + match.substr(1); }); } function mightBeEmail(s) { return typeof s === "string" && s.indexOf("@") !== -1; } const redacted = "REDACTED (Potential Email Address)"; function redactEmail(string) { if (mightBeEmail(string)) { console.warn("This arg looks like an email address, redacting."); return redacted; } return string; } function format(s = "", titleCase = true, redactingEmail = true) { let _str = s || ""; if (titleCase) _str = toTitleCase(s); if (redactingEmail) _str = redactEmail(_str); return _str; } //#endregion //#region src/ga4.ts var GA4 = class { isInitialized; _testMode; _currentMeasurementId; _hasLoadedGA; _isQueuing; _queueGtag; constructor() { this.reset(); } reset = () => { this.isInitialized = false; this._testMode = false; this._currentMeasurementId = ""; this._hasLoadedGA = false; this._isQueuing = false; this._queueGtag = []; }; _gtag = (...args) => { if (!this._testMode) if (this._isQueuing) this._queueGtag.push(args); else gtag(...args); else this._queueGtag.push(args); }; gtag(...args) { this._gtag(...args); } _loadGA = (GA_MEASUREMENT_ID, nonce, gtagUrl = "https://www.googletagmanager.com/gtag/js") => { if (typeof window === "undefined" || typeof document === "undefined") return; if (!this._hasLoadedGA) { const script = document.createElement("script"); script.async = true; script.src = `${gtagUrl}?id=${GA_MEASUREMENT_ID}`; if (nonce) script.setAttribute("nonce", nonce); document.body.appendChild(script); window.dataLayer = window.dataLayer || []; window.gtag = function gtag() { window.dataLayer.push(arguments); }; this._hasLoadedGA = true; } }; _toGtagOptions = (gaOptions) => { if (!gaOptions) return; const mapFields = { cookieUpdate: "cookie_update", cookieExpires: "cookie_expires", cookieDomain: "cookie_domain", cookieFlags: "cookie_flags", userId: "user_id", clientId: "client_id", anonymizeIp: "anonymize_ip", contentGroup1: "content_group1", contentGroup2: "content_group2", contentGroup3: "content_group3", contentGroup4: "content_group4", contentGroup5: "content_group5", allowAdFeatures: "allow_google_signals", allowAdPersonalizationSignals: "allow_ad_personalization_signals", nonInteraction: "non_interaction", page: "page_path", hitCallback: "event_callback" }; return Object.entries(gaOptions).reduce((prev, [key, value]) => { if (mapFields[key]) prev[mapFields[key]] = value; else prev[key] = value; return prev; }, {}); }; /** * * @param {InitOptions[]|string} GA_MEASUREMENT_ID * @param {Object} [options] * @param {string} [options.nonce] * @param {boolean} [options.testMode=false] * @param {string} [options.gtagUrl=https://www.googletagmanager.com/gtag/js] * @param {GaOptions|any} [options.gaOptions] * @param {Object} [options.gtagOptions] New parameter */ initialize = (GA_MEASUREMENT_ID, options = {}) => { if (!GA_MEASUREMENT_ID) throw new Error("Require GA_MEASUREMENT_ID"); const initConfigs = typeof GA_MEASUREMENT_ID === "string" ? [{ trackingId: GA_MEASUREMENT_ID }] : GA_MEASUREMENT_ID; this._currentMeasurementId = initConfigs[0].trackingId; const { gaOptions, gtagOptions, nonce, testMode = false, gtagUrl } = options; this._testMode = testMode; if (!testMode) this._loadGA(this._currentMeasurementId, nonce, gtagUrl); if (!this.isInitialized) { this._gtag("js", /* @__PURE__ */ new Date()); initConfigs.forEach((config) => { const mergedGtagOptions = { ...this._toGtagOptions({ ...gaOptions, ...config.gaOptions }), ...gtagOptions, ...config.gtagOptions }; if (Object.keys(mergedGtagOptions).length) this._gtag("config", config.trackingId, mergedGtagOptions); else this._gtag("config", config.trackingId); }); } this.isInitialized = true; if (!testMode) { const queues = [...this._queueGtag]; this._queueGtag = []; this._isQueuing = false; while (queues.length) { const queue = queues.shift(); this._gtag(...queue); if (queue[0] === "get") this._isQueuing = true; } } }; set = (fieldsObject) => { if (!fieldsObject) { console.warn("`fieldsObject` is required in .set()"); return; } if (typeof fieldsObject !== "object") { console.warn("Expected `fieldsObject` arg to be an Object"); return; } if (Object.keys(fieldsObject).length === 0) console.warn("empty `fieldsObject` given to .set()"); this._gaCommand("set", fieldsObject); }; _gaCommandSendEvent = (eventCategory, eventAction, eventLabel, eventValue, fieldsObject) => { this._gtag("event", eventAction, { event_category: eventCategory, event_label: eventLabel, value: eventValue, ...fieldsObject && { non_interaction: fieldsObject.nonInteraction }, ...this._toGtagOptions(fieldsObject) }); }; _gaCommandSendEventParameters = (...args) => { if (typeof args[0] === "string") this._gaCommandSendEvent(...args.slice(1)); else { const { eventCategory, eventAction, eventLabel, eventValue, hitType, ...rest } = args[0]; this._gaCommandSendEvent(eventCategory, eventAction, eventLabel, eventValue, rest); } }; _gaCommandSendTiming = (timingCategory, timingVar, timingValue, timingLabel) => { this._gtag("event", "timing_complete", { name: timingVar, value: timingValue, event_category: timingCategory, event_label: timingLabel }); }; _gaCommandSendPageview = (page, fieldsObject) => { if (fieldsObject && Object.keys(fieldsObject).length) { const { title, location, ...rest } = this._toGtagOptions(fieldsObject) || {}; this._gtag("event", "page_view", { ...page && { page_path: page }, ...title && { page_title: title }, ...location && { page_location: location }, ...rest }); } else if (page) this._gtag("event", "page_view", { page_path: page }); else this._gtag("event", "page_view"); }; _gaCommandSendPageviewParameters = (...args) => { if (typeof args[0] === "string") this._gaCommandSendPageview(...args.slice(1)); else { const { page, hitType, ...rest } = args[0]; this._gaCommandSendPageview(page, rest); } }; _gaCommandSend = (...args) => { const hitType = typeof args[0] === "string" ? args[0] : args[0].hitType; switch (hitType) { case "event": this._gaCommandSendEventParameters(...args); break; case "pageview": this._gaCommandSendPageviewParameters(...args); break; case "timing": this._gaCommandSendTiming(...args.slice(1)); break; case "screenview": case "transaction": case "item": case "social": case "exception": console.warn(`Unsupported send command: ${hitType}`); break; default: console.warn(`Send command doesn't exist: ${hitType}`); } }; _gaCommandSet = (...args) => { if (typeof args[0] === "string") args[0] = { [args[0]]: args[1] }; this._gtag("set", this._toGtagOptions(args[0])); }; _gaCommand = (command, ...args) => { switch (command) { case "send": this._gaCommandSend(...args); break; case "set": this._gaCommandSet(...args); break; default: console.warn(`Command doesn't exist: ${command}`); } }; ga = (...args) => { if (typeof args[0] === "string") this._gaCommand(...args); else { const [readyCallback] = args; this._gtag("get", this._currentMeasurementId, "client_id", (clientId) => { this._isQueuing = false; const queues = this._queueGtag; readyCallback({ get: (property) => property === "clientId" ? clientId : property === "trackingId" ? this._currentMeasurementId : property === "apiVersion" ? "1" : void 0 }); while (queues.length) { const queue = queues.shift(); this._gtag(...queue); } }); this._isQueuing = true; } return this.ga; }; /** * @param {UaEventOptions|string} optionsOrName * @param {Object} [params] */ event = (optionsOrName, params) => { if (typeof optionsOrName === "string") this._gtag("event", optionsOrName, this._toGtagOptions(params)); else { const { action, category, label, value, nonInteraction, transport } = optionsOrName; if (!category || !action) { console.warn("args.category AND args.action are required in event()"); return; } const fieldObject = { hitType: "event", eventCategory: format(category), eventAction: format(action) }; if (label) fieldObject.eventLabel = format(label); if (typeof value !== "undefined") if (typeof value !== "number") console.warn("Expected `args.value` arg to be a Number."); else fieldObject.eventValue = value; if (typeof nonInteraction !== "undefined") if (typeof nonInteraction !== "boolean") console.warn("`args.nonInteraction` must be a boolean."); else fieldObject.nonInteraction = nonInteraction; if (typeof transport !== "undefined") if (typeof transport !== "string") console.warn("`args.transport` must be a string."); else { if ([ "beacon", "xhr", "image" ].indexOf(transport) === -1) console.warn("`args.transport` must be either one of these values: `beacon`, `xhr` or `image`"); fieldObject.transport = transport; } this._gaCommand("send", fieldObject); } }; send = (fieldObject) => { this._gaCommand("send", fieldObject); }; }; var ga4_default = new GA4(); //#endregion //#region src/index.ts const ReactGAImplementation = GA4; var src_default = ga4_default; //#endregion export { ReactGAImplementation, src_default as default };