Skip to content

Commit

Permalink
feat!: disableAnalytics and enableAnalytics
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Feb 27, 2024
1 parent 3bd75af commit 998fee6
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 101 deletions.
2 changes: 1 addition & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ export default defineNuxtConfig({
modules: ['../src/module.ts'],

gtag: {
enabled: false,
tags: ['G-ZZZZZZZZZZ'],
initialConsent: false,
},

typescript: {
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function disableAnalytics(id: string) {
(window as any)[`ga-disable-${id}`] = true
}

export function enableAnalytics(id: string) {
const key = `ga-disable-${id}`

if (key in window)
delete (window as any)[key]
}
124 changes: 73 additions & 51 deletions src/runtime/composables/useGtag.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,69 @@
import { withQuery } from 'ufo'
import { disableGtag, enableGtag, gtag, initGtag } from '../gtag'
import { gtag, initGtag } from '../gtag'
import { disableAnalytics as _disableAnalytics, enableAnalytics as _enableAnalytics } from '../analytics'
import type { ModuleOptions } from '../../module'
import type { Gtag, UseGtagConsentOptions } from '../types'
import type { Gtag } from '../types'
import { resolveTags } from '../utils'
import { useHead, useRuntimeConfig } from '#imports'

export function useGtag() {
const options = useRuntimeConfig().public.gtag as Required<ModuleOptions>
const tags = resolveTags(options)
const rawTags = resolveTags(options)

let _gtag: Gtag
// Return a noop function if this composable is called on the server.
if (process.server)
if (import.meta.server)
_gtag = () => {}
else if (process.client)
else if (import.meta.client)
_gtag = gtag

const setConsent = ({
id,
hasConsent = true,
}: UseGtagConsentOptions) => {
if (process.client) {
const _tags = [...tags]
let tag = _tags.find(tag => tag.id === id)
const getTag = (id?: string) => {
const tags = [...rawTags]
let tag = tags.find(tag => tag.id === id)

if (!tag) {
if (id) {
tag = { id }
_tags.unshift(tag)
}
else {
tag = _tags[0]
}
if (!tag) {
if (id) {
tag = { id }
tags.unshift(tag)
}

if (!tag) {
console.error('[nuxt-gtag] Missing Google tag ID')
return
else {
tag = tags[0]
}
}

if (!tag)
console.error('[nuxt-gtag] Missing Google tag ID')

if (!hasConsent) {
disableGtag(tag.id)
return { tag, tags }
}

/**
* Manually initialize the Google tag library.
*
* @remarks
* If no custom Google tag ID is provided, the default Google tag ID from the module options will be used.
*/
const initialize = (
/**
* In case you want to initialize a custom Google tag ID. Make sure to set
* `enabled` to `false` in the module options beforehand.
*/
id?: string,
) => {
if (import.meta.client) {
const { tag, tags } = getTag(id)
if (!tag)
return
}

// Initialize `dataLayer` if the client plugin didn't initialize it
// (because no ID was provided in the module options).
if (!window.dataLayer)
initGtag({ tags: _tags })
initGtag({ tags })

// If the `dataLayer` has more than two items
// it is considered to be initialized.
if (window.dataLayer!.length > 2) {
// Re-enable Google Analytics if it was disabled before.
enableGtag(tag.id)
if (window.dataLayer!.length > 2)
return
}

// Inject the Google tag script.
useHead({
Expand All @@ -64,30 +72,44 @@ export function useGtag() {
}
}

const grantConsent = (
/**
* In case you want to initialize a custom Google tag ID. Make sure to set
* `initialConsent` to `false` in the module options beforehand.
*/
id?: string,
) => {
setConsent({ id, hasConsent: true })
/**
* Disable Google Analytics measurement.
*
* @remarks
* The `gtag.js` library includes a `window['ga-disable-GA_MEASUREMENT_ID']`
* property that, when set to `true`, disables `gtag.js` from sending data to Google Analytics.
*
* @see {@link https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out Disable Google Analytics measurement}
*/
function disableAnalytics(id?: string) {
if (import.meta.client) {
const { tag } = getTag(id)
if (tag)
_disableAnalytics(tag.id)
}
}

const revokeConsent = (
/**
* In case you want to initialize a custom Google tag ID. Make sure to set
* `initialConsent` to `false` in the module options beforehand.
*/
id?: string,
) => {
setConsent({ id, hasConsent: false })
/**
* Enable Google Analytics measurement if it was previously disabled.
*
* @remarks
* The `gtag.js` library includes a `window['ga-disable-GA_MEASUREMENT_ID']`
* property that, when set to `true`, disables `gtag.js` from sending data to Google Analytics.
*
* @see {@link https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out Disable Google Analytics measurement}
*/
function enableAnalytics(id?: string) {
if (import.meta.client) {
const { tag } = getTag(id)
if (tag)
_enableAnalytics(tag.id)
}
}

return {
gtag: _gtag!,
setConsent,
grantConsent,
revokeConsent,
initialize,
disableAnalytics,
enableAnalytics,
}
}
7 changes: 3 additions & 4 deletions src/runtime/composables/useTrackEvent.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { ControlParams, EventNames, EventParams } from '../types'
import type { GtagCommands } from '../types'
import { useGtag } from './useGtag'

export function useTrackEvent(
eventName: EventNames | (string & Record<never, never>),
eventParams?: ControlParams | EventParams | Record<string, any>,
...args: GtagCommands['event']
) {
const { gtag } = useGtag()
gtag('event', eventName, eventParams)
gtag('event', ...args)
}
29 changes: 2 additions & 27 deletions src/runtime/gtag.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { GoogleTagOptions } from './types'

// eslint-disable-next-line unused-imports/no-unused-vars
export function gtag(command: string, ...args: any[]) {
export function gtag(...args: any[]) {
// eslint-disable-next-line prefer-rest-params
window.dataLayer?.push(arguments)
}
Expand All @@ -13,36 +13,11 @@ export function initGtag({ tags }: { tags: GoogleTagOptions[] }) {
window.dataLayer = window.dataLayer || []

for (const tag of tags) {
for (const command of tag.initCommands ?? []) {
// @ts-expect-error: Tuple type
for (const command of tag.initCommands ?? [])
gtag(...command)
}
}

gtag('js', new Date())
for (const tag of tags)
gtag('config', tag.id, tag.config)
}

/**
* Disable the Google tag if it is a Google Analytics property.
*
* @remarks
* The Google tag library includes a `window['ga-disable-GA_MEASUREMENT_ID']`
* property that, when set to `true`, turns off the Google tag from sending data.
*
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
*/
export function disableGtag(id: string) {
(window as any)[`ga-disable-${id}`] = true
}

/**
* Enable Google Analytics if it was disabled before.
*/
export function enableGtag(id: string) {
const key = `ga-disable-${id}`

if (key in window)
delete (window as any)[key]
}
2 changes: 1 addition & 1 deletion src/runtime/plugin.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default defineNuxtPlugin({

initGtag({ tags })

if (!options.initialConsent)
if (!options.enabled)
return

// Sanitize loading strategy to be either `async` or `defer`
Expand Down
18 changes: 1 addition & 17 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface GoogleTagOptions {
* @default undefined
*/
initCommands?: {
[K in keyof GtagCommands]: [K, ...GtagCommands[K]];
[K in keyof GtagCommands]: [K, ...GtagCommands[K]]
}[keyof GtagCommands][]
/**
* Additional configuration for the Google tag ID, to be set during initialization of the tag ID with the `config' command.
Expand All @@ -34,32 +34,16 @@ export interface GoogleTagOptions {
config?: GtagCommands['config'][1]
}

export interface UseGtagConsentOptions {
/**
* Whether to accept or decline the consent.
*
* @default true
*/
hasConsent?: boolean
/**
* In case you want to initialize a custom Google tag ID. Make sure to set
* `initialConsent` to `false` in the module options beforehand.
*/
id?: string
}

export interface GtagCommands {
config: [targetId: string, config?: ControlParams | EventParams | ConfigParams | CustomParams]
set: [targetId: string, config: CustomParams | boolean | string] | [config: CustomParams]
js: [config: Date]
// eslint-disable-next-line ts/ban-types
event: [eventName: EventNames | (string & {}), eventParams?: ControlParams | EventParams | CustomParams]
get: [
targetId: string,
fieldName: FieldNames | string,
callback?: (field?: string | CustomParams) => any,
]
// eslint-disable-next-line ts/ban-types
consent: [consentArg: ConsentArg | (string & {}), consentParams: ConsentParams]
}

Expand Down

0 comments on commit 998fee6

Please sign in to comment.