Skip to content

Commit 09e3c2f

Browse files
authored
fix(tpc): support customizing dataLayer variable
1 parent 3c8e6c4 commit 09e3c2f

File tree

6 files changed

+100
-61
lines changed

6 files changed

+100
-61
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
"shiki": "^1.10.3",
9090
"sirv": "^2.0.4",
9191
"std-env": "^3.7.0",
92-
"third-party-capital": "^1.1.1",
92+
"third-party-capital": "^2.1.1",
9393
"ufo": "^1.5.3",
9494
"unimport": "^3.7.2",
9595
"unplugin": "^1.11.0",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/generateTpcScripts.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ export interface TpcDescriptor {
1010
label: string
1111
tpcKey: string
1212
tpcData: Output
13-
tpcTypeImport: string
13+
tpcTypeAugmentation?: string
14+
tpcTypesImport?: string[]
1415
key: string
1516
registry?: any
1617
scriptInput?: UseScriptInput
1718
performanceMarkFeature?: string
18-
returnUse?: string
19+
useBody?: string
1920
returnStub?: string
2021
clientInit?: string
2122
defaultOptions?: Record<string, unknown>
@@ -28,14 +29,12 @@ const scripts: Array<TpcDescriptor> = [
2829
label: 'Google Tag Manager',
2930
tpcKey: 'gtm',
3031
tpcData: GoogleTagManagerData as Output,
31-
tpcTypeImport: 'GoogleTagManagerApi',
32+
tpcTypeAugmentation: 'GoogleTagManagerApi',
33+
tpcTypesImport: ['DataLayer'],
3234
key: 'googleTagManager',
3335
performanceMarkFeature: 'nuxt-third-parties-gtm',
34-
returnUse: '{ dataLayer: window.dataLayers[options.dataLayerName!], google_tag_manager: window.google_tag_manager }',
36+
useBody: 'return { dataLayer: (window as any)[options.l ?? "dataLayer"] as DataLayer, google_tag_manager: window.google_tag_manager }',
3537
returnStub: 'fn === \'dataLayer\' ? [] : void 0',
36-
defaultOptions: {
37-
dataLayerName: 'defaultGtm',
38-
},
3938
},
4039
// GA
4140
{
@@ -44,14 +43,11 @@ const scripts: Array<TpcDescriptor> = [
4443
tpcKey: 'gtag',
4544
tpcData: GooglaAnalyticsData as Output,
4645
key: 'googleAnalytics',
47-
tpcTypeImport: 'GoogleAnalyticsApi',
46+
tpcTypesImport: ['DataLayer', 'GTag'],
4847
performanceMarkFeature: 'nuxt-third-parties-ga',
49-
returnUse: '{ dataLayer: window.dataLayers[options.dataLayerName!], gtag: window.gtag }',
48+
useBody: 'const gtag: GTag = function (...args:Parameters<GTag>) { \n((window as any)[options.l ?? "dataLayer"] as DataLayer).push(args);} as GTag\nreturn { dataLayer: (window as any)[options.l ?? "dataLayer"] as DataLayer,\n gtag }',
5049
// allow dataLayer to be accessed on the server
5150
returnStub: 'fn === \'dataLayer\' ? [] : void 0',
52-
defaultOptions: {
53-
dataLayerName: 'defaultGa',
54-
},
5551
}]
5652

5753
export async function generate() {

scripts/utils.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,53 @@ export async function generateTpcContent(input: TpcDescriptor) {
2424
'import { withQuery } from \'ufo\'',
2525
'import { useRegistryScript } from \'#nuxt-scripts-utils\'',
2626
'import type { RegistryScriptInput } from \'#nuxt-scripts\'',
27-
`import type { ${input.tpcTypeImport} } from 'third-party-capital'`,
2827
])
28+
const tpcTypes = new Set<string>()
2929

3030
const chunks: string[] = []
3131
const functionBody: string[] = []
3232

33+
if (input.tpcTypeAugmentation) {
34+
tpcTypes.add(input.tpcTypeAugmentation)
35+
36+
chunks.push(`
37+
declare global {
38+
interface Window extends ${input.tpcTypeAugmentation} {}
39+
}`)
40+
}
41+
42+
if (input.tpcTypesImport) {
43+
for (const typeImport of input.tpcTypesImport) {
44+
tpcTypes.add(typeImport)
45+
}
46+
}
47+
48+
if (tpcTypes.size) {
49+
imports.add(genImport('third-party-capital', [...tpcTypes]))
50+
}
51+
3352
if (input.defaultOptions) {
3453
imports.add(genImport('defu', ['defu']))
3554
functionBody.push(`_options = defu(_options, ${JSON.stringify(input.defaultOptions)})`)
3655
}
3756

3857
const params = [...new Set(input.tpcData.scripts?.map(s => s.params || []).flat() || [])]
58+
const optionalParams = [...new Set(input.tpcData.scripts?.map(s => Object.keys(s.optionalParams) || []).flat() || [])]
3959

40-
if (params.length) {
60+
if (params.length || optionalParams.length) {
4161
const validatorImports = new Set<string>(['object', 'string'])
62+
if (optionalParams.length) {
63+
validatorImports.add('optional')
64+
}
65+
66+
const properties = params.filter(p => !optionalParams.includes(p)).map(p => `${p}: string()`).concat(optionalParams.map(o => `${o}: optional(string())`))
4267
// need schema validation from tpc
43-
chunks.push(`export const ${titleKey}Options = object({${params.map((p) => {
44-
if (input.defaultOptions && p in input.defaultOptions) {
45-
validatorImports.add('optional')
46-
return `${p}: optional(string())`
47-
}
48-
return `${p}: string()`
49-
})}})`)
68+
chunks.push(`export const ${titleKey}Options = object({
69+
${properties.join(',\n')}
70+
})`)
5071
imports.add(genImport('#nuxt-scripts-validator', [...validatorImports]))
5172
}
5273

53-
chunks.push(`
54-
declare global {
55-
interface Window extends ${input.tpcTypeImport} {}
56-
}`)
57-
5874
const clientInitCode: string[] = []
5975

6076
if (input.tpcData.stylesheets) {
@@ -66,7 +82,7 @@ declare global {
6682

6783
for (const script of input.tpcData.scripts) {
6884
if ('code' in script)
69-
clientInitCode.push(replaceTokenToRuntime(script.code))
85+
clientInitCode.push(replaceTokenToRuntime(script.code, script.optionalParams))
7086

7187
if (script === mainScript)
7288
continue
@@ -78,21 +94,33 @@ declare global {
7894

7995
chunks.push(`export type ${titleKey}Input = RegistryScriptInput${params.length ? `<typeof ${titleKey}Options>` : ''}`)
8096

97+
if (input.useBody) {
98+
chunks.push(`
99+
function use(options: ${titleKey}Input) {
100+
${input.useBody}
101+
}
102+
`)
103+
}
104+
105+
const srcQueries = [...new Set<string>([...mainScript.params, ...Object.keys(mainScript.optionalParams)])].map(p => `${p}: options?.${p}`)
106+
81107
chunks.push(`
82-
export function ${input.registry.import!.name}<T extends ${input.tpcTypeImport}>(_options?: ${titleKey}Input) {
108+
export function ${input.registry.import!.name}(_options?: ${titleKey}Input) {
83109
${functionBody.join('\n')}
84-
return useRegistryScript${params.length ? `<T, typeof ${titleKey}Options>` : ''}(_options?.key || '${input.key}', options => ({
110+
return useRegistryScript<${input.useBody ? `ReturnType<typeof use>` : `Record<string | symbol, any>`},${params.length ? `typeof ${titleKey}Options` : ''}>(_options?.key || '${input.key}', options => ({
85111
scriptInput: {
86-
src: withQuery('${mainScript.url}', {${mainScript.params?.map(p => `${p}: options?.${p}`)}})
112+
src: withQuery('${mainScript.url}', {${srcQueries.join(', ')}}),
87113
},
88114
schema: import.meta.dev ? ${titleKey}Options : undefined,
89115
scriptOptions: {
90-
use: () => { return ${input.returnUse} },
116+
${input.useBody ? `use: () => use(options),` : ''}
91117
stub: import.meta.client ? undefined : ({fn}) => { return ${input.returnStub}},
92118
${input.performanceMarkFeature ? `performanceMarkFeature: ${JSON.stringify(input.performanceMarkFeature)},` : ''}
93119
${mainScriptOptions ? `...(${JSON.stringify(mainScriptOptions)})` : ''}
94120
},
95121
// eslint-disable-next-line
122+
// @ts-ignore
123+
// eslint-disable-next-line
96124
${clientInitCode.length ? `clientInit: import.meta.server ? undefined : () => {${clientInitCode.join('\n')}},` : ''}
97125
}), _options)
98126
}`)
@@ -102,8 +130,10 @@ ${functionBody.join('\n')}
102130
return chunks.join('\n')
103131
}
104132

105-
function replaceTokenToRuntime(code: string) {
106-
return code.split(';').map(c => c.replaceAll(/'?\{\{(.*?)\}\}'?/g, 'options.$1!')).join(';')
133+
function replaceTokenToRuntime(code: string, defaultValues?: Record<string, string | number | undefined>) {
134+
return code.split(';').map(c => c.replaceAll(/'?\{\{(.*?)\}\}'?/g, (_, token) => {
135+
return `options?.${token} ${defaultValues?.[token] ? `?? ${JSON.stringify(defaultValues?.[token])}` : ''}`
136+
})).join(';')
107137
}
108138

109139
function getScriptInputOption(script: Script): HeadEntryOptions | undefined {
Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,39 @@
11
// WARNING: This file is automatically generated, do not manually modify.
22
import { withQuery } from 'ufo'
3-
import type { GoogleAnalyticsApi } from 'third-party-capital'
4-
import { defu } from 'defu'
3+
import type { DataLayer, GTag } from 'third-party-capital'
54
import { useRegistryScript } from '#nuxt-scripts-utils'
65
import type { RegistryScriptInput } from '#nuxt-scripts'
76
import { object, string, optional } from '#nuxt-scripts-validator'
87

9-
export const GoogleAnalyticsOptions = object({ id: string(), dataLayerName: optional(string()) })
8+
export const GoogleAnalyticsOptions = object({
9+
id: string(),
10+
l: optional(string()),
11+
})
12+
export type GoogleAnalyticsInput = RegistryScriptInput<typeof GoogleAnalyticsOptions>
1013

11-
declare global {
12-
interface Window extends GoogleAnalyticsApi {}
14+
function use(options: GoogleAnalyticsInput) {
15+
const gtag: GTag = function (...args: Parameters<GTag>) {
16+
((window as any)[options.l ?? 'dataLayer'] as DataLayer).push(args)
17+
} as GTag
18+
return { dataLayer: (window as any)[options.l ?? 'dataLayer'] as DataLayer,
19+
gtag }
1320
}
14-
export type GoogleAnalyticsInput = RegistryScriptInput<typeof GoogleAnalyticsOptions>
1521

16-
export function useScriptGoogleAnalytics<T extends GoogleAnalyticsApi>(_options?: GoogleAnalyticsInput) {
17-
_options = defu(_options, { dataLayerName: 'defaultGa' })
18-
return useRegistryScript<T, typeof GoogleAnalyticsOptions>(_options?.key || 'googleAnalytics', options => ({
22+
export function useScriptGoogleAnalytics(_options?: GoogleAnalyticsInput) {
23+
return useRegistryScript<ReturnType<typeof use>, typeof GoogleAnalyticsOptions>(_options?.key || 'googleAnalytics', options => ({
1924
scriptInput: {
20-
src: withQuery('https://www.googletagmanager.com/gtag/js', { id: options?.id }),
25+
src: withQuery('https://www.googletagmanager.com/gtag/js', { id: options?.id, l: options?.l }),
2126
},
2227
schema: import.meta.dev ? GoogleAnalyticsOptions : undefined,
2328
scriptOptions: {
24-
use: () => { return { dataLayer: window.dataLayers[options.dataLayerName!], gtag: window.gtag } },
29+
use: () => use(options),
2530
stub: import.meta.client ? undefined : ({ fn }) => { return fn === 'dataLayer' ? [] : void 0 },
2631
performanceMarkFeature: 'nuxt-third-parties-ga',
2732
...({ tagPriority: 1 }),
2833
},
2934
// eslint-disable-next-line
30-
clientInit: import.meta.server ? undefined : () => {window.dataLayers=window.dataLayers||{};window.dataLayers[options.dataLayerName!]=window.dataLayers[options.dataLayerName!]||[];window.gtag=function gtag(){window.dataLayer.push(arguments);};window.gtag('js',new Date());window.gtag('config',options.id!)},
35+
// @ts-ignore
36+
// eslint-disable-next-line
37+
clientInit: import.meta.server ? undefined : () => {window[options?.l ?? "dataLayer"]=window[options?.l ?? "dataLayer"]||[];window[options?.l ?? "dataLayer"].push({'js':new Date()});window[options?.l ?? "dataLayer"].push({'config':options?.id })},
3138
}), _options)
3239
}
Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,38 @@
11
// WARNING: This file is automatically generated, do not manually modify.
22
import { withQuery } from 'ufo'
3-
import type { GoogleTagManagerApi } from 'third-party-capital'
4-
import { defu } from 'defu'
3+
import type { GoogleTagManagerApi, DataLayer } from 'third-party-capital'
54
import { useRegistryScript } from '#nuxt-scripts-utils'
65
import type { RegistryScriptInput } from '#nuxt-scripts'
76
import { object, string, optional } from '#nuxt-scripts-validator'
87

9-
export const GoogleTagManagerOptions = object({ id: string(), dataLayerName: optional(string()) })
10-
118
declare global {
129
interface Window extends GoogleTagManagerApi {}
1310
}
11+
export const GoogleTagManagerOptions = object({
12+
id: string(),
13+
l: optional(string()),
14+
})
1415
export type GoogleTagManagerInput = RegistryScriptInput<typeof GoogleTagManagerOptions>
1516

16-
export function useScriptGoogleTagManager<T extends GoogleTagManagerApi>(_options?: GoogleTagManagerInput) {
17-
_options = defu(_options, { dataLayerName: 'defaultGtm' })
18-
return useRegistryScript<T, typeof GoogleTagManagerOptions>(_options?.key || 'googleTagManager', options => ({
17+
function use(options: GoogleTagManagerInput) {
18+
return { dataLayer: (window as any)[options.l ?? 'dataLayer'] as DataLayer, google_tag_manager: window.google_tag_manager }
19+
}
20+
21+
export function useScriptGoogleTagManager(_options?: GoogleTagManagerInput) {
22+
return useRegistryScript<ReturnType<typeof use>, typeof GoogleTagManagerOptions>(_options?.key || 'googleTagManager', options => ({
1923
scriptInput: {
20-
src: withQuery('https://www.googletagmanager.com/gtm.js', { id: options?.id }),
24+
src: withQuery('https://www.googletagmanager.com/gtm.js', { id: options?.id, l: options?.l }),
2125
},
2226
schema: import.meta.dev ? GoogleTagManagerOptions : undefined,
2327
scriptOptions: {
24-
use: () => { return { dataLayer: window.dataLayers[options.dataLayerName!], google_tag_manager: window.google_tag_manager } },
28+
use: () => use(options),
2529
stub: import.meta.client ? undefined : ({ fn }) => { return fn === 'dataLayer' ? [] : void 0 },
2630
performanceMarkFeature: 'nuxt-third-parties-gtm',
2731
...({ tagPriority: 1 }),
2832
},
2933
// eslint-disable-next-line
30-
clientInit: import.meta.server ? undefined : () => {window.dataLayers=window.dataLayers||{};window.dataLayers[options.dataLayerName!]=window.dataLayers[options.dataLayerName!]||[];window.dataLayers[options.dataLayerName!].push({'gtm.start':new Date().getTime(),event:'gtm.js'});},
34+
// @ts-ignore
35+
// eslint-disable-next-line
36+
clientInit: import.meta.server ? undefined : () => {window[options?.l ?? "dataLayer"]=window[options?.l ?? "dataLayer"]||[];window[options?.l ?? "dataLayer"].push({'gtm.start':new Date().getTime(),event:'gtm.js'});},
3137
}), _options)
3238
}

0 commit comments

Comments
 (0)