@@ -2,6 +2,7 @@ import type { ModuleOptions } from '../../packages/script/src/module'
22import type { CrispApi } from '../../packages/script/src/runtime/registry/crisp'
33import type { DefaultEventName } from '../../packages/script/src/runtime/registry/google-analytics'
44import type {
5+ NuxtConfigScriptRegistry ,
56 NuxtUseScriptOptions ,
67 RegistryScriptInput ,
78 ScriptRegistry ,
@@ -10,14 +11,72 @@ import type {
1011import { describe , expectTypeOf , it } from 'vitest'
1112
1213describe ( 'module options registry' , ( ) => {
13- it ( 'registry entries are typed' , ( ) => {
14- // Check specific registry keys have proper types (not any)
15- // Using specific keys because the index signature `[key: \`${string}-npm\`]`
16- // causes `keyof ScriptRegistry` to include template literals which resolve to any
17- type Registry = NonNullable < ModuleOptions [ 'registry' ] >
18- expectTypeOf < Registry [ 'googleAnalytics' ] > ( ) . not . toBeAny ( )
14+ type Registry = NonNullable < ModuleOptions [ 'registry' ] >
15+
16+ it ( 'all registry entries are typed, not any' , ( ) => {
17+ // Every built-in registry key must resolve to its specific type, not `any`.
18+ // NuxtConfigScriptRegistry is an interface (not an intersection), so explicit
19+ // properties inherited via `extends` always take priority over the index signature.
20+ expectTypeOf < Registry [ 'bingUet' ] > ( ) . not . toBeAny ( )
21+ expectTypeOf < Registry [ 'blueskyEmbed' ] > ( ) . not . toBeAny ( )
22+ expectTypeOf < Registry [ 'carbonAds' ] > ( ) . not . toBeAny ( )
23+ expectTypeOf < Registry [ 'crisp' ] > ( ) . not . toBeAny ( )
1924 expectTypeOf < Registry [ 'clarity' ] > ( ) . not . toBeAny ( )
25+ expectTypeOf < Registry [ 'cloudflareWebAnalytics' ] > ( ) . not . toBeAny ( )
26+ expectTypeOf < Registry [ 'databuddyAnalytics' ] > ( ) . not . toBeAny ( )
27+ expectTypeOf < Registry [ 'metaPixel' ] > ( ) . not . toBeAny ( )
28+ expectTypeOf < Registry [ 'fathomAnalytics' ] > ( ) . not . toBeAny ( )
29+ expectTypeOf < Registry [ 'instagramEmbed' ] > ( ) . not . toBeAny ( )
30+ expectTypeOf < Registry [ 'plausibleAnalytics' ] > ( ) . not . toBeAny ( )
31+ expectTypeOf < Registry [ 'googleAdsense' ] > ( ) . not . toBeAny ( )
32+ expectTypeOf < Registry [ 'googleAnalytics' ] > ( ) . not . toBeAny ( )
33+ expectTypeOf < Registry [ 'googleMaps' ] > ( ) . not . toBeAny ( )
34+ expectTypeOf < Registry [ 'googleRecaptcha' ] > ( ) . not . toBeAny ( )
35+ expectTypeOf < Registry [ 'googleSignIn' ] > ( ) . not . toBeAny ( )
36+ expectTypeOf < Registry [ 'lemonSqueezy' ] > ( ) . not . toBeAny ( )
37+ expectTypeOf < Registry [ 'googleTagManager' ] > ( ) . not . toBeAny ( )
38+ expectTypeOf < Registry [ 'hotjar' ] > ( ) . not . toBeAny ( )
39+ expectTypeOf < Registry [ 'intercom' ] > ( ) . not . toBeAny ( )
40+ expectTypeOf < Registry [ 'paypal' ] > ( ) . not . toBeAny ( )
41+ expectTypeOf < Registry [ 'posthog' ] > ( ) . not . toBeAny ( )
42+ expectTypeOf < Registry [ 'matomoAnalytics' ] > ( ) . not . toBeAny ( )
43+ expectTypeOf < Registry [ 'mixpanelAnalytics' ] > ( ) . not . toBeAny ( )
44+ expectTypeOf < Registry [ 'rybbitAnalytics' ] > ( ) . not . toBeAny ( )
45+ expectTypeOf < Registry [ 'redditPixel' ] > ( ) . not . toBeAny ( )
46+ expectTypeOf < Registry [ 'segment' ] > ( ) . not . toBeAny ( )
2047 expectTypeOf < Registry [ 'stripe' ] > ( ) . not . toBeAny ( )
48+ expectTypeOf < Registry [ 'tiktokPixel' ] > ( ) . not . toBeAny ( )
49+ expectTypeOf < Registry [ 'xEmbed' ] > ( ) . not . toBeAny ( )
50+ expectTypeOf < Registry [ 'xPixel' ] > ( ) . not . toBeAny ( )
51+ expectTypeOf < Registry [ 'snapchatPixel' ] > ( ) . not . toBeAny ( )
52+ expectTypeOf < Registry [ 'youtubePlayer' ] > ( ) . not . toBeAny ( )
53+ expectTypeOf < Registry [ 'vercelAnalytics' ] > ( ) . not . toBeAny ( )
54+ expectTypeOf < Registry [ 'vimeoPlayer' ] > ( ) . not . toBeAny ( )
55+ expectTypeOf < Registry [ 'umamiAnalytics' ] > ( ) . not . toBeAny ( )
56+ expectTypeOf < Registry [ 'gravatar' ] > ( ) . not . toBeAny ( )
57+ expectTypeOf < Registry [ 'npm' ] > ( ) . not . toBeAny ( )
58+ } )
59+
60+ it ( 'known keys resolve to their exact entry type, not the catch-all' , ( ) => {
61+ // Known registry keys must resolve to NuxtConfigScriptRegistryEntry<SpecificInput>,
62+ // not the index signature's `any` catch-all.
63+ // The interface approach guarantees this: inherited properties from `extends` always
64+ // take priority over the index signature.
65+ type GoogleMapsEntry = NuxtConfigScriptRegistry [ 'googleMaps' ]
66+ type CatchAllEntry = NuxtConfigScriptRegistry [ string ]
67+ // Known key must NOT equal the catch-all
68+ expectTypeOf < GoogleMapsEntry > ( ) . not . toEqualTypeOf < CatchAllEntry > ( )
69+
70+ // Verify specific input properties survive (not collapsed to unknown)
71+ type ObjectForm < K extends keyof Registry > = Exclude < Registry [ K ] , boolean | 'mock' | undefined >
72+ expectTypeOf < ObjectForm < 'googleMaps' > [ 'apiKey' ] > ( ) . not . toBeNever ( )
73+ expectTypeOf < ObjectForm < 'googleAnalytics' > [ 'id' ] > ( ) . not . toBeNever ( )
74+ expectTypeOf < ObjectForm < 'clarity' > [ 'id' ] > ( ) . not . toBeNever ( )
75+ } )
76+
77+ it ( 'registry allows unknown keys as catch-all' , ( ) => {
78+ // Unknown keys fall through to the index signature (any), so custom scripts work
79+ expectTypeOf < Registry [ 'my-custom-script' ] > ( ) . toBeAny ( )
2180 } )
2281} )
2382
0 commit comments