Skip to content

Commit 1b7d96d

Browse files
feat(tpc): build-time third-party-capital composables (#81)
Co-authored-by: Julien Huang <[email protected]>
1 parent e9af679 commit 1b7d96d

File tree

13 files changed

+1695
-296
lines changed

13 files changed

+1695
-296
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
"@unhead/vue",
5555
"@unhead/schema",
5656
"#nuxt-scripts",
57-
"#nuxt-scripts-validator"
57+
"#nuxt-scripts-validator",
58+
"third-party-capital",
59+
"knitwork"
5860
]
5961
},
6062
"dependencies": {
@@ -81,7 +83,7 @@
8183
"shiki": "^1.6.2",
8284
"sirv": "^2.0.4",
8385
"std-env": "^3.7.0",
84-
"third-party-capital": "^1.0.28",
86+
"third-party-capital": "^1.0.30",
8587
"ufo": "^1.5.3",
8688
"unimport": "^3.7.2",
8789
"unplugin": "^1.10.1",
@@ -99,6 +101,7 @@
99101
"bumpp": "^9.4.1",
100102
"changelogen": "^0.5.5",
101103
"eslint": "9.4.0",
104+
"knitwork": "^1.1.0",
102105
"nuxt": "^3.11.2",
103106
"playwright-core": "^1.44.1",
104107
"typescript": "^5.4.5",

playground/pages/third-parties/google-tag-manager.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ $script.then(({ google_tag_manager, dataLayer }) => {
3939
<div>
4040
<ClientOnly>
4141
<div>
42-
status: {{ $script.status.value }}
42+
status: {{ $script.status }}
4343
</div>
4444
</ClientOnly>
4545
</div>

pnpm-lock.yaml

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

src/module.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import type {
2525
RegistryScript,
2626
RegistryScripts,
2727
} from './runtime/types'
28+
import addGoogleAnalyticsRegistry from './tpc/google-analytics'
29+
import addGoogleTagManagerRegistry from './tpc/google-tag-manager'
2830

2931
export interface ModuleOptions {
3032
/**
@@ -74,6 +76,12 @@ export interface ModuleHooks {
7476
'scripts:registry': (registry: RegistryScripts) => void | Promise<void>
7577
}
7678

79+
declare module '@nuxt/schema' {
80+
interface NuxtHooks {
81+
'scripts:registry': ModuleHooks['scripts:registry']
82+
}
83+
}
84+
7785
export default defineNuxtModule<ModuleOptions>({
7886
meta: {
7987
name: '@nuxt/scripts',
@@ -125,9 +133,13 @@ export default defineNuxtModule<ModuleOptions>({
125133
})
126134

127135
const scripts = registry(resolve)
136+
137+
addGoogleAnalyticsRegistry()
138+
addGoogleTagManagerRegistry()
139+
128140
nuxt.hooks.hook('modules:done', async () => {
129141
const registryScripts = [...scripts]
130-
// @ts-expect-error runtime
142+
131143
await nuxt.hooks.callHook('scripts:registry', registryScripts)
132144
const withComposables = registryScripts.filter(i => !!i.import?.name) as Required<RegistryScript>[]
133145
addImports(withComposables.map((i) => {

src/registry.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,15 @@ import type { IntercomInput } from './runtime/registry/intercom'
44
import type { SegmentInput } from './runtime/registry/segment'
55
import type { NpmInput } from './runtime/registry/npm'
66
import type { PlausibleAnalyticsInput } from './runtime/registry/plausible-analytics'
7-
import type { GoogleAnalyticsInput } from './runtime/registry/google-analytics'
87
import type { RegistryScripts } from './runtime/types'
98
import type { GoogleAdsenseInput } from './runtime/registry/google-adsense'
109

1110
// avoid nuxt/kit dependency here so we can use in docs
1211

1312
export const registry: (resolve?: (s: string) => string) => RegistryScripts = (resolve?: (s: string) => string) => {
1413
resolve = resolve || ((s: string) => s)
14+
1515
return [
16-
// analytics
17-
{
18-
label: 'Google Analytics',
19-
category: 'analytics',
20-
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="28.85" height="32" viewBox="0 0 256 284"><path fill="#F9AB00" d="M256.003 247.933a35.224 35.224 0 0 1-39.376 35.161c-18.044-2.67-31.266-18.371-30.826-36.606V36.845C185.365 18.591 198.62 2.881 216.687.24a35.221 35.221 0 0 1 39.316 35.16z"/><path fill="#E37400" d="M35.101 213.193c19.386 0 35.101 15.716 35.101 35.101c0 19.386-15.715 35.101-35.101 35.101S0 267.68 0 248.295c0-19.386 15.715-35.102 35.101-35.102m92.358-106.387c-19.477 1.068-34.59 17.406-34.137 36.908v94.285c0 25.588 11.259 41.122 27.755 44.433a35.161 35.161 0 0 0 42.146-34.56V142.089a35.222 35.222 0 0 0-35.764-35.282"/></svg>`,
21-
scriptBundling: (options?: GoogleAnalyticsInput) => withQuery('https://www.googletagmanager.com/gtag/js', {
22-
id: options?.id,
23-
}),
24-
import: {
25-
name: 'useScriptGoogleAnalytics',
26-
from: resolve('./runtime/registry/google-analytics'),
27-
},
28-
},
2916
{
3017
label: 'Plausible Analytics',
3118
category: 'analytics',
@@ -69,16 +56,6 @@ export const registry: (resolve?: (s: string) => string) => RegistryScripts = (r
6956
from: resolve('./runtime/registry/matomo-analytics'),
7057
},
7158
},
72-
// tracking
73-
{
74-
label: 'Google Tag Manager',
75-
category: 'tracking',
76-
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="#8AB4F8" d="m150.262 245.516l-44.437-43.331l95.433-97.454l46.007 45.091z"/><path fill="#4285F4" d="M150.45 53.938L106.176 8.731L9.36 104.629c-12.48 12.48-12.48 32.713 0 45.207l95.36 95.986l45.09-42.182l-72.654-76.407z"/><path fill="#8AB4F8" d="m246.625 105.37l-96-96c-12.494-12.494-32.756-12.494-45.25 0c-12.495 12.495-12.495 32.757 0 45.252l96 96c12.494 12.494 32.756 12.494 45.25 0c12.495-12.495 12.495-32.757 0-45.251"/><circle cx="127.265" cy="224.731" r="31.273" fill="#246FDB"/></svg>`,
77-
import: {
78-
name: 'useScriptGoogleTagManager',
79-
from: resolve('./runtime/registry/google-tag-manager'),
80-
},
81-
},
8259
{
8360
label: 'Segment',
8461
scriptBundling: (options?: SegmentInput) => {

src/runtime/registry/google-analytics.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/runtime/registry/google-tag-manager.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/runtime/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import type { MetaPixelInput } from './registry/meta-pixel'
1010
import type { FathomAnalyticsInput } from './registry/fathom-analytics'
1111
import type { HotjarInput } from './registry/hotjar'
1212
import type { IntercomInput } from './registry/intercom'
13-
import type { GoogleAnalyticsInput } from './registry/google-analytics'
1413
import type { GoogleMapsInput } from './registry/google-maps'
15-
import type { GoogleTagManagerInput } from './registry/google-tag-manager'
1614
import type { MatomoAnalyticsInput } from './registry/matomo-analytics'
1715
import type { StripeInput } from './registry/stripe'
1816
import type { VimeoPlayerInput } from './registry/vimeo-player'
@@ -21,6 +19,8 @@ import type { YouTubePlayerInput } from './registry/youtube-player'
2119
import type { PlausibleAnalyticsInput } from './registry/plausible-analytics'
2220
import type { NpmInput } from './registry/npm'
2321
import type { LemonSqueezyInput } from './registry/lemon-squeezy'
22+
import type { Input as GoogleTagManagerInput } from '#build/nuxt-scripts/tpc/google-tag-manager'
23+
import type { Input as GoogleAnalyticsInput } from '#build/nuxt-scripts/tpc/google-analytics'
2424

2525
export type NuxtUseScriptOptions<T = any> = Omit<UseScriptOptions<T>, 'trigger'> & {
2626
/**

src/tpc/google-analytics.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { addImports, addTemplate, useNuxt } from '@nuxt/kit'
2+
import type { Output } from 'third-party-capital'
3+
import { GooglaAnalyticsData, GoogleAnalytics } from 'third-party-capital'
4+
import type { RegistryScript } from '../runtime/types'
5+
import { getTpcScriptContent } from './utils'
6+
7+
export default function googleAnalitycsRegistry() {
8+
const nuxt = useNuxt()
9+
const { dst } = addTemplate({
10+
getContents() {
11+
return getTpcScriptContent({
12+
data: GooglaAnalyticsData as Output,
13+
scriptFunctionName: 'useScriptGoogleAnalytics',
14+
use: () => {
15+
return { dataLayer: window.dataLayer, gtag: window.gtag }
16+
},
17+
// allow dataLayer to be accessed on the server
18+
stub: ({ fn }) => {
19+
return fn === 'dataLayer' ? [] : undefined
20+
},
21+
tpcKey: 'gtag',
22+
tpcTypeImport: 'GoogleAnalyticsApi',
23+
augmentWindowTypes: true,
24+
})
25+
},
26+
filename: 'nuxt-scripts/tpc/google-analytics.ts',
27+
})
28+
29+
addImports({
30+
from: dst,
31+
name: 'useScriptGoogleAnalytics',
32+
})
33+
const registry: RegistryScript = {
34+
label: 'Google Tag Manager',
35+
category: 'tracking',
36+
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="#8AB4F8" d="m150.262 245.516l-44.437-43.331l95.433-97.454l46.007 45.091z"/><path fill="#4285F4" d="M150.45 53.938L106.176 8.731L9.36 104.629c-12.48 12.48-12.48 32.713 0 45.207l95.36 95.986l45.09-42.182l-72.654-76.407z"/><path fill="#8AB4F8" d="m246.625 105.37l-96-96c-12.494-12.494-32.756-12.494-45.25 0c-12.495 12.495-12.495 32.757 0 45.252l96 96c12.494 12.494 32.756 12.494 45.25 0c12.495-12.495 12.495-32.757 0-45.251"/><circle cx="127.265" cy="224.731" r="31.273" fill="#246FDB"/></svg>`,
37+
import: {
38+
name: 'useScriptGoogleAnalytics',
39+
from: dst,
40+
},
41+
scriptBundling(options) {
42+
const data = GoogleAnalytics(options)
43+
const mainScript = data.scripts?.find(({ key }) => key === 'gtag')
44+
45+
if (mainScript && 'url' in mainScript && mainScript.url)
46+
return mainScript.url
47+
48+
return false
49+
},
50+
}
51+
nuxt.hook('scripts:registry', (scripts) => {
52+
scripts.push(registry)
53+
})
54+
}

src/tpc/google-tag-manager.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { addImports, addTemplate, useNuxt } from '@nuxt/kit'
2+
import type { Output } from 'third-party-capital'
3+
import { GoogleTagManager, GoogleTagManagerData } from 'third-party-capital'
4+
5+
import type { RegistryScript } from '../runtime/types'
6+
import { getTpcScriptContent } from './utils'
7+
8+
export default function googleTagManagerRegistry() {
9+
const nuxt = useNuxt()
10+
11+
const { dst } = addTemplate({
12+
getContents() {
13+
return getTpcScriptContent({
14+
data: GoogleTagManagerData as Output,
15+
scriptFunctionName: 'useScriptGoogleTagManager',
16+
use: () => {
17+
return { dataLayer: window.dataLayer, google_tag_manager: window.google_tag_manager }
18+
},
19+
stub: ({ fn }) => {
20+
return fn === 'dataLayer' ? [] : undefined
21+
},
22+
tpcKey: 'gtm',
23+
tpcTypeImport: 'GoogleTagManagerApi',
24+
augmentWindowTypes: true,
25+
})
26+
},
27+
filename: 'nuxt-scripts/tpc/google-tag-manager.ts',
28+
})
29+
30+
addImports({
31+
from: dst,
32+
name: 'useScriptGoogleTagManager',
33+
})
34+
35+
const registry: RegistryScript = {
36+
label: 'Google Tag Manager',
37+
category: 'tracking',
38+
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="#8AB4F8" d="m150.262 245.516l-44.437-43.331l95.433-97.454l46.007 45.091z"/><path fill="#4285F4" d="M150.45 53.938L106.176 8.731L9.36 104.629c-12.48 12.48-12.48 32.713 0 45.207l95.36 95.986l45.09-42.182l-72.654-76.407z"/><path fill="#8AB4F8" d="m246.625 105.37l-96-96c-12.494-12.494-32.756-12.494-45.25 0c-12.495 12.495-12.495 32.757 0 45.252l96 96c12.494 12.494 32.756 12.494 45.25 0c12.495-12.495 12.495-32.757 0-45.251"/><circle cx="127.265" cy="224.731" r="31.273" fill="#246FDB"/></svg>`,
39+
import: {
40+
name: 'useScriptGoogleTagManager',
41+
from: dst,
42+
},
43+
scriptBundling(options) {
44+
const data = GoogleTagManager(options)
45+
const mainScript = data.scripts?.find(({ key }) => key === 'gtm')
46+
47+
if (mainScript && 'url' in mainScript && mainScript.url)
48+
return mainScript.url
49+
50+
return false
51+
},
52+
}
53+
54+
nuxt.hook('scripts:registry', (scripts) => {
55+
scripts.push(registry)
56+
})
57+
}

0 commit comments

Comments
 (0)