@nuxt/scripts 1.0.0-beta.2 → 1.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +3 -3
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/{DvH517bE.js → BlmrFwhD.js} +1 -1
  5. package/dist/client/_nuxt/{DfLgoB--.js → BwCYQWJt.js} +1 -1
  6. package/dist/client/_nuxt/DvbTvDd0.js +162 -0
  7. package/dist/client/_nuxt/{B66N9HCo.js → ZrewjUYk.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/6660a023-888d-415f-b66d-ce774e6f8f11.json +1 -0
  10. package/dist/client/_nuxt/entry.CACgbLJl.css +1 -0
  11. package/dist/client/_nuxt/error-404.CHeaW3dp.css +1 -0
  12. package/dist/client/_nuxt/error-500.DvOvWme_.css +1 -0
  13. package/dist/client/index.html +1 -1
  14. package/dist/module.d.mts +27 -18
  15. package/dist/module.d.ts +178 -0
  16. package/dist/module.json +1 -1
  17. package/dist/module.mjs +763 -526
  18. package/dist/registry.d.ts +6 -0
  19. package/dist/registry.mjs +109 -21
  20. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +2 -2
  21. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +7 -7
  22. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +2 -2
  23. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue +6 -6
  24. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue +7 -7
  25. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue +6 -6
  26. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.vue +12 -12
  27. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue +6 -6
  28. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.d.vue.ts +1 -1
  29. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +6 -6
  30. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +1 -1
  31. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue +5 -5
  32. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.vue +7 -7
  33. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.vue +7 -7
  34. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.vue +7 -7
  35. package/dist/runtime/components/ScriptCrisp.vue +1 -1
  36. package/dist/runtime/components/ScriptGoogleAdsense.vue +1 -1
  37. package/dist/runtime/components/ScriptGravatar.d.vue.ts +22 -0
  38. package/dist/runtime/components/ScriptGravatar.vue +46 -0
  39. package/dist/runtime/components/ScriptGravatar.vue.d.ts +22 -0
  40. package/dist/runtime/components/ScriptInstagramEmbed.vue +1 -1
  41. package/dist/runtime/components/ScriptIntercom.vue +4 -3
  42. package/dist/runtime/components/ScriptPayPalButtons.d.vue.ts +43 -32
  43. package/dist/runtime/components/ScriptPayPalButtons.vue +48 -79
  44. package/dist/runtime/components/ScriptPayPalButtons.vue.d.ts +43 -32
  45. package/dist/runtime/components/ScriptPayPalMessages.d.vue.ts +37 -23
  46. package/dist/runtime/components/ScriptPayPalMessages.vue +46 -50
  47. package/dist/runtime/components/ScriptPayPalMessages.vue.d.ts +37 -23
  48. package/dist/runtime/components/ScriptStripePricingTable.vue +2 -2
  49. package/dist/runtime/components/ScriptVimeoPlayer.d.vue.ts +9 -0
  50. package/dist/runtime/components/ScriptVimeoPlayer.vue +13 -10
  51. package/dist/runtime/components/ScriptVimeoPlayer.vue.d.ts +9 -0
  52. package/dist/runtime/components/ScriptXEmbed.d.vue.ts +1 -1
  53. package/dist/runtime/components/ScriptXEmbed.vue +1 -1
  54. package/dist/runtime/components/ScriptXEmbed.vue.d.ts +1 -1
  55. package/dist/runtime/components/ScriptYouTubePlayer.d.vue.ts +2 -2
  56. package/dist/runtime/components/ScriptYouTubePlayer.vue +11 -5
  57. package/dist/runtime/components/ScriptYouTubePlayer.vue.d.ts +2 -2
  58. package/dist/runtime/composables/useScript.js +11 -6
  59. package/dist/runtime/composables/useScriptEventPage.js +2 -2
  60. package/dist/runtime/composables/useScriptTriggerConsent.js +1 -1
  61. package/dist/runtime/composables/useScriptTriggerElement.js +1 -1
  62. package/dist/runtime/composables/useScriptTriggerIdleTimeout.js +1 -1
  63. package/dist/runtime/registry/clarity.d.ts +10 -15
  64. package/dist/runtime/registry/clarity.js +22 -31
  65. package/dist/runtime/registry/cloudflare-web-analytics.d.ts +2 -13
  66. package/dist/runtime/registry/cloudflare-web-analytics.js +2 -14
  67. package/dist/runtime/registry/crisp.d.ts +9 -39
  68. package/dist/runtime/registry/crisp.js +2 -33
  69. package/dist/runtime/registry/databuddy-analytics.d.ts +2 -35
  70. package/dist/runtime/registry/databuddy-analytics.js +20 -45
  71. package/dist/runtime/registry/fathom-analytics.d.ts +6 -25
  72. package/dist/runtime/registry/fathom-analytics.js +2 -24
  73. package/dist/runtime/registry/google-adsense.d.ts +2 -10
  74. package/dist/runtime/registry/google-adsense.js +2 -11
  75. package/dist/runtime/registry/google-analytics.d.ts +3 -5
  76. package/dist/runtime/registry/google-analytics.js +3 -8
  77. package/dist/runtime/registry/google-maps.d.ts +3 -9
  78. package/dist/runtime/registry/google-maps.js +2 -8
  79. package/dist/runtime/registry/google-recaptcha.d.ts +2 -6
  80. package/dist/runtime/registry/google-recaptcha.js +4 -12
  81. package/dist/runtime/registry/google-sign-in.d.ts +2 -13
  82. package/dist/runtime/registry/google-sign-in.js +2 -22
  83. package/dist/runtime/registry/google-tag-manager.d.ts +3 -28
  84. package/dist/runtime/registry/google-tag-manager.js +4 -27
  85. package/dist/runtime/registry/gravatar.d.ts +25 -0
  86. package/dist/runtime/registry/gravatar.js +32 -0
  87. package/dist/runtime/registry/hotjar.d.ts +3 -5
  88. package/dist/runtime/registry/hotjar.js +2 -5
  89. package/dist/runtime/registry/instagram-embed.d.ts +2 -17
  90. package/dist/runtime/registry/instagram-embed.js +4 -19
  91. package/dist/runtime/registry/intercom.d.ts +3 -11
  92. package/dist/runtime/registry/intercom.js +2 -12
  93. package/dist/runtime/registry/matomo-analytics.d.ts +2 -11
  94. package/dist/runtime/registry/matomo-analytics.js +3 -12
  95. package/dist/runtime/registry/meta-pixel.d.ts +3 -5
  96. package/dist/runtime/registry/meta-pixel.js +2 -4
  97. package/dist/runtime/registry/npm.d.ts +2 -6
  98. package/dist/runtime/registry/npm.js +2 -9
  99. package/dist/runtime/registry/paypal.d.ts +4 -25
  100. package/dist/runtime/registry/paypal.js +3 -66
  101. package/dist/runtime/registry/plausible-analytics.js +18 -13
  102. package/dist/runtime/registry/posthog.d.ts +10 -11
  103. package/dist/runtime/registry/posthog.js +7 -20
  104. package/dist/runtime/registry/reddit-pixel.d.ts +4 -5
  105. package/dist/runtime/registry/reddit-pixel.js +2 -4
  106. package/dist/runtime/registry/rybbit-analytics.d.ts +2 -14
  107. package/dist/runtime/registry/rybbit-analytics.js +7 -19
  108. package/dist/runtime/registry/schemas.d.ts +946 -0
  109. package/dist/runtime/registry/schemas.js +901 -0
  110. package/dist/runtime/registry/segment.d.ts +2 -5
  111. package/dist/runtime/registry/segment.js +2 -5
  112. package/dist/runtime/registry/snapchat-pixel.d.ts +3 -32
  113. package/dist/runtime/registry/snapchat-pixel.js +2 -20
  114. package/dist/runtime/registry/stripe.d.ts +3 -4
  115. package/dist/runtime/registry/stripe.js +2 -4
  116. package/dist/runtime/registry/tiktok-pixel.d.ts +3 -6
  117. package/dist/runtime/registry/tiktok-pixel.js +2 -6
  118. package/dist/runtime/registry/umami-analytics.d.ts +2 -31
  119. package/dist/runtime/registry/umami-analytics.js +2 -36
  120. package/dist/runtime/registry/vercel-analytics.d.ts +29 -0
  121. package/dist/runtime/registry/vercel-analytics.js +84 -0
  122. package/dist/runtime/registry/vimeo-player.d.ts +2 -2
  123. package/dist/runtime/registry/vimeo-player.js +1 -1
  124. package/dist/runtime/registry/x-embed.d.ts +2 -16
  125. package/dist/runtime/registry/x-embed.js +2 -17
  126. package/dist/runtime/registry/x-pixel.d.ts +3 -6
  127. package/dist/runtime/registry/x-pixel.js +2 -5
  128. package/dist/runtime/registry/youtube-player.d.ts +7 -7
  129. package/dist/runtime/registry/youtube-player.js +1 -1
  130. package/dist/runtime/server/google-static-maps-proxy.js +1 -1
  131. package/dist/runtime/server/{sw-handler.d.ts → gravatar-proxy.d.ts} +1 -1
  132. package/dist/runtime/server/gravatar-proxy.js +62 -0
  133. package/dist/runtime/server/instagram-embed-asset.js +2 -1
  134. package/dist/runtime/server/instagram-embed-image.js +2 -1
  135. package/dist/runtime/server/instagram-embed.js +22 -13
  136. package/dist/runtime/server/proxy-handler.js +161 -117
  137. package/dist/runtime/server/utils/privacy.d.ts +45 -1
  138. package/dist/runtime/server/utils/privacy.js +103 -40
  139. package/dist/runtime/server/x-embed.js +3 -2
  140. package/dist/runtime/types.d.ts +35 -24
  141. package/dist/runtime/utils/pure.d.ts +0 -4
  142. package/dist/runtime/utils/pure.js +0 -67
  143. package/dist/runtime/utils.d.ts +3 -3
  144. package/dist/runtime/utils.js +12 -7
  145. package/dist/shared/scripts.Crpn87WB.mjs +318 -0
  146. package/dist/stats.d.mts +39 -0
  147. package/dist/stats.d.ts +39 -0
  148. package/dist/stats.mjs +772 -0
  149. package/dist/types-source.d.mts +19 -0
  150. package/dist/types-source.d.ts +19 -0
  151. package/dist/types-source.mjs +975 -0
  152. package/package.json +42 -31
  153. package/dist/client/_nuxt/B8XOar-X.js +0 -162
  154. package/dist/client/_nuxt/builds/meta/133a46c5-a5c1-4a63-87d1-037947a5bcdb.json +0 -1
  155. package/dist/client/_nuxt/entry.D45OuV0w.css +0 -1
  156. package/dist/client/_nuxt/error-404.B57D-jUQ.css +0 -1
  157. package/dist/client/_nuxt/error-500.DTHUW7BI.css +0 -1
  158. package/dist/runtime/components/ScriptPayPalMarks.d.vue.ts +0 -52
  159. package/dist/runtime/components/ScriptPayPalMarks.vue +0 -69
  160. package/dist/runtime/components/ScriptPayPalMarks.vue.d.ts +0 -52
  161. package/dist/runtime/plugins/sw-register.client.d.ts +0 -2
  162. package/dist/runtime/plugins/sw-register.client.js +0 -12
  163. package/dist/runtime/server/sw-handler.js +0 -25
  164. package/dist/runtime/sw/proxy-sw.template.d.ts +0 -1
  165. package/dist/runtime/sw/proxy-sw.template.js +0 -54
package/dist/module.mjs CHANGED
@@ -1,79 +1,36 @@
1
- import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, extendRouteRules, tryUseNuxt, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addImports, addComponentsDir, addTemplate, addServerHandler, addPluginTemplate, addBuildPlugin } from '@nuxt/kit';
2
- import { existsSync, readFileSync } from 'node:fs';
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, extendViteConfig, useLogger, addPluginTemplate, addServerHandler, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addImports, addComponentsDir, addTemplate, addBuildPlugin } from '@nuxt/kit';
3
3
  import { defu } from 'defu';
4
+ import { join, resolve, relative } from 'pathe';
4
5
  import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
6
+ import { lazyEventHandler, eventHandler, createError } from 'h3';
7
+ import { fetch, $fetch } from 'ofetch';
8
+ import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
9
+ import { createStorage } from 'unstorage';
10
+ import fsDriver from 'unstorage/drivers/fs-lite';
5
11
  import { addCustomTab } from '@nuxt/devtools-kit';
12
+ import { isCI, provider } from 'std-env';
13
+ import { parseAndWalk, ScopeTracker, walk, ScopeTrackerFunctionParam, ScopeTrackerVariable } from 'oxc-walker';
14
+ import { createUnplugin } from 'unplugin';
15
+ import { pathToFileURL } from 'node:url';
6
16
  import { createHash } from 'node:crypto';
7
17
  import fsp from 'node:fs/promises';
8
- import { createUnplugin } from 'unplugin';
18
+ import { colors } from 'consola/utils';
9
19
  import MagicString from 'magic-string';
10
- import { parseAndWalk } from 'oxc-walker';
11
- import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
12
20
  import { hash } from 'ohash';
13
- import { join, resolve, relative } from 'pathe';
14
- import { colors } from 'consola/utils';
15
- import { fetch, $fetch } from 'ofetch';
16
- import { lazyEventHandler, eventHandler, createError } from 'h3';
17
- import { createStorage } from 'unstorage';
18
- import fsDriver from 'unstorage/drivers/fs-lite';
19
- import { rewriteScriptUrls } from '../dist/runtime/utils/pure.js';
20
- import { pathToFileURL } from 'node:url';
21
- import { isCI, provider } from 'std-env';
21
+ import { g as getAllProxyConfigs, r as routesToInterceptRules, a as getProxyConfig } from './shared/scripts.Crpn87WB.mjs';
22
22
  import { registry } from './registry.mjs';
23
23
 
24
- const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
25
- const DEVTOOLS_UI_LOCAL_PORT = 3300;
26
-
27
- async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
28
- const clientPath = await resolve("./client");
29
- const isProductionBuild = existsSync(clientPath);
30
- if (isProductionBuild) {
31
- nuxt.hook("vite:serverCreated", async (server) => {
32
- const sirv = await import('sirv').then((r) => r.default || r);
33
- server.middlewares.use(
34
- DEVTOOLS_UI_ROUTE,
35
- sirv(clientPath, { dev: true, single: true })
36
- );
37
- });
38
- } else {
39
- extendViteConfig((config) => {
40
- config.server = config.server || {};
41
- config.server.proxy = config.server.proxy || {};
42
- config.server.proxy[DEVTOOLS_UI_ROUTE] = {
43
- target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
44
- changeOrigin: true,
45
- followRedirects: true,
46
- rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
47
- };
48
- });
49
- }
50
- addCustomTab({
51
- // unique identifier
52
- name: "nuxt-scripts",
53
- // title to display in the tab
54
- title: "Scripts",
55
- // any icon from Iconify, or a URL to an image
56
- icon: "carbon:script",
57
- // iframe view
58
- view: {
59
- type: "iframe",
60
- src: DEVTOOLS_UI_ROUTE
61
- }
62
- });
63
- }
64
-
65
- const logger = useLogger("@nuxt/scripts");
66
-
67
24
  const renderedScript = /* @__PURE__ */ new Map();
68
25
  const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
69
- const bundleStorage = () => {
26
+ function bundleStorage() {
70
27
  const nuxt = tryUseNuxt();
71
28
  return createStorage({
72
29
  driver: fsDriver({
73
30
  base: resolve(nuxt?.options.rootDir || "", "node_modules/.cache/nuxt/scripts")
74
31
  })
75
32
  });
76
- };
33
+ }
77
34
  function setupPublicAssetStrategy(options = {}) {
78
35
  const assetsBaseURL = options.prefix || "/_scripts";
79
36
  const nuxt = useNuxt();
@@ -123,179 +80,308 @@ function setupPublicAssetStrategy(options = {}) {
123
80
  };
124
81
  }
125
82
 
126
- function buildProxyConfig(collectPrefix) {
127
- return {
128
- googleAnalytics: {
129
- rewrite: [
130
- // Modern gtag.js uses www.google.com/g/collect
131
- { from: "www.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
132
- // Older gtag.js constructs URLs dynamically: "https://"+(subdomain)+".google-analytics.com/g/collect"
133
- // We need to catch the suffix pattern with leading dot
134
- { from: ".google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
135
- { from: ".analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
136
- // Full domain patterns for static URLs
137
- { from: "www.google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
138
- { from: "analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
139
- // Legacy endpoints still used by some scripts
140
- { from: "www.google-analytics.com", to: `${collectPrefix}/ga` },
141
- { from: "analytics.google.com", to: `${collectPrefix}/ga` },
142
- // DoubleClick tracking (used by GA4 for ads/conversions)
143
- { from: "stats.g.doubleclick.net/g/collect", to: `${collectPrefix}/ga/g/collect` },
144
- { from: "stats.g.doubleclick.net", to: `${collectPrefix}/ga-dc` },
145
- // Google Ads/Syndication tracking
146
- { from: "pagead2.googlesyndication.com", to: `${collectPrefix}/ga-syn` },
147
- { from: "www.googleadservices.com", to: `${collectPrefix}/ga-ads` },
148
- { from: "googleads.g.doubleclick.net", to: `${collectPrefix}/ga-gads` }
149
- ],
150
- routes: {
151
- [`${collectPrefix}/ga/**`]: { proxy: "https://www.google-analytics.com/**" },
152
- [`${collectPrefix}/ga-dc/**`]: { proxy: "https://stats.g.doubleclick.net/**" },
153
- [`${collectPrefix}/ga-syn/**`]: { proxy: "https://pagead2.googlesyndication.com/**" },
154
- [`${collectPrefix}/ga-ads/**`]: { proxy: "https://www.googleadservices.com/**" },
155
- [`${collectPrefix}/ga-gads/**`]: { proxy: "https://googleads.g.doubleclick.net/**" }
156
- }
157
- },
158
- googleTagManager: {
159
- rewrite: [
160
- { from: "www.googletagmanager.com", to: `${collectPrefix}/gtm` }
161
- ],
162
- routes: {
163
- [`${collectPrefix}/gtm/**`]: { proxy: "https://www.googletagmanager.com/**" }
164
- }
165
- },
166
- metaPixel: {
167
- rewrite: [
168
- // SDK script loading
169
- { from: "connect.facebook.net", to: `${collectPrefix}/meta` },
170
- // Tracking pixel endpoint (www and non-www)
171
- { from: "www.facebook.com/tr", to: `${collectPrefix}/meta-tr` },
172
- { from: "facebook.com/tr", to: `${collectPrefix}/meta-tr` },
173
- // Additional Meta tracking domains
174
- { from: "pixel.facebook.com", to: `${collectPrefix}/meta-px` },
175
- { from: "www.facebook.com/plugins", to: `${collectPrefix}/meta-plugins` }
176
- ],
177
- routes: {
178
- [`${collectPrefix}/meta/**`]: { proxy: "https://connect.facebook.net/**" },
179
- [`${collectPrefix}/meta-tr/**`]: { proxy: "https://www.facebook.com/tr/**" },
180
- [`${collectPrefix}/meta-px/**`]: { proxy: "https://pixel.facebook.com/**" },
181
- [`${collectPrefix}/meta-plugins/**`]: { proxy: "https://www.facebook.com/plugins/**" }
182
- }
183
- },
184
- tiktokPixel: {
185
- rewrite: [
186
- { from: "analytics.tiktok.com", to: `${collectPrefix}/tiktok` }
187
- ],
188
- routes: {
189
- [`${collectPrefix}/tiktok/**`]: { proxy: "https://analytics.tiktok.com/**" }
190
- }
191
- },
192
- segment: {
193
- rewrite: [
194
- { from: "api.segment.io", to: `${collectPrefix}/segment` },
195
- { from: "cdn.segment.com", to: `${collectPrefix}/segment-cdn` }
196
- ],
197
- routes: {
198
- [`${collectPrefix}/segment/**`]: { proxy: "https://api.segment.io/**" },
199
- [`${collectPrefix}/segment-cdn/**`]: { proxy: "https://cdn.segment.com/**" }
200
- }
201
- },
202
- xPixel: {
203
- rewrite: [
204
- { from: "analytics.twitter.com", to: `${collectPrefix}/x` },
205
- { from: "t.co", to: `${collectPrefix}/x-t` }
206
- ],
207
- routes: {
208
- [`${collectPrefix}/x/**`]: { proxy: "https://analytics.twitter.com/**" },
209
- [`${collectPrefix}/x-t/**`]: { proxy: "https://t.co/**" }
210
- }
211
- },
212
- snapchatPixel: {
213
- rewrite: [
214
- { from: "tr.snapchat.com", to: `${collectPrefix}/snap` }
215
- ],
216
- routes: {
217
- [`${collectPrefix}/snap/**`]: { proxy: "https://tr.snapchat.com/**" }
218
- }
219
- },
220
- redditPixel: {
221
- rewrite: [
222
- { from: "alb.reddit.com", to: `${collectPrefix}/reddit` }
223
- ],
224
- routes: {
225
- [`${collectPrefix}/reddit/**`]: { proxy: "https://alb.reddit.com/**" }
226
- }
227
- },
228
- clarity: {
229
- rewrite: [
230
- // Main clarity domain
231
- { from: "www.clarity.ms", to: `${collectPrefix}/clarity` },
232
- // Script loader (the actual SDK is loaded from here)
233
- { from: "scripts.clarity.ms", to: `${collectPrefix}/clarity-scripts` },
234
- // Data collection endpoint
235
- { from: "d.clarity.ms", to: `${collectPrefix}/clarity-data` },
236
- // Event collection endpoint
237
- { from: "e.clarity.ms", to: `${collectPrefix}/clarity-events` }
238
- ],
239
- routes: {
240
- [`${collectPrefix}/clarity/**`]: { proxy: "https://www.clarity.ms/**" },
241
- [`${collectPrefix}/clarity-scripts/**`]: { proxy: "https://scripts.clarity.ms/**" },
242
- [`${collectPrefix}/clarity-data/**`]: { proxy: "https://d.clarity.ms/**" },
243
- [`${collectPrefix}/clarity-events/**`]: { proxy: "https://e.clarity.ms/**" }
83
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
84
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
85
+
86
+ async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
87
+ const clientPath = await resolve("./client");
88
+ const isProductionBuild = existsSync(clientPath);
89
+ if (isProductionBuild) {
90
+ nuxt.hook("vite:serverCreated", async (server) => {
91
+ const sirv = await import('sirv').then((r) => r.default || r);
92
+ server.middlewares.use(
93
+ DEVTOOLS_UI_ROUTE,
94
+ sirv(clientPath, { dev: true, single: true })
95
+ );
96
+ });
97
+ } else {
98
+ extendViteConfig((config) => {
99
+ config.server = config.server || {};
100
+ config.server.proxy = config.server.proxy || {};
101
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
102
+ target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
103
+ changeOrigin: true,
104
+ followRedirects: true,
105
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
106
+ };
107
+ });
108
+ }
109
+ addCustomTab({
110
+ // unique identifier
111
+ name: "nuxt-scripts",
112
+ // title to display in the tab
113
+ title: "Scripts",
114
+ // any icon from Iconify, or a URL to an image
115
+ icon: "carbon:script",
116
+ // iframe view
117
+ view: {
118
+ type: "iframe",
119
+ src: DEVTOOLS_UI_ROUTE
120
+ }
121
+ });
122
+ }
123
+
124
+ const logger = useLogger("@nuxt/scripts");
125
+
126
+ const AUTO_INJECT_DEFS = [
127
+ {
128
+ registryKey: "posthog",
129
+ configField: "apiHost",
130
+ computeValue: (collectPrefix, config) => {
131
+ const region = config.region || "us";
132
+ return region === "eu" ? `${collectPrefix}/ph-eu` : `${collectPrefix}/ph`;
133
+ }
134
+ },
135
+ {
136
+ registryKey: "plausibleAnalytics",
137
+ configField: "endpoint",
138
+ computeValue: (collectPrefix) => `${collectPrefix}/plausible/api/event`
139
+ },
140
+ {
141
+ registryKey: "umamiAnalytics",
142
+ configField: "hostUrl",
143
+ computeValue: (collectPrefix) => `${collectPrefix}/umami`
144
+ },
145
+ {
146
+ registryKey: "rybbitAnalytics",
147
+ configField: "analyticsHost",
148
+ computeValue: (collectPrefix) => `${collectPrefix}/rybbit/api`
149
+ },
150
+ {
151
+ registryKey: "databuddyAnalytics",
152
+ configField: "apiUrl",
153
+ computeValue: (collectPrefix) => `${collectPrefix}/databuddy-api`
154
+ }
155
+ ];
156
+ function autoInjectProxyEndpoints(registry, runtimeConfig, collectPrefix) {
157
+ for (const def of AUTO_INJECT_DEFS) {
158
+ const entry = registry[def.registryKey];
159
+ if (!entry || typeof entry !== "object")
160
+ continue;
161
+ const config = Array.isArray(entry) ? entry[0] : entry;
162
+ if (!config || config[def.configField])
163
+ continue;
164
+ config[def.configField] = def.computeValue(collectPrefix, config);
165
+ const rtScripts = runtimeConfig.public?.scripts;
166
+ const rtEntry = rtScripts?.[def.registryKey];
167
+ if (rtEntry && typeof rtEntry === "object") {
168
+ const rtConfig = Array.isArray(rtEntry) ? rtEntry[0] : rtEntry;
169
+ if (rtConfig)
170
+ rtConfig[def.configField] = config[def.configField];
171
+ }
172
+ }
173
+ }
174
+
175
+ function generateInterceptPluginContents(interceptRules) {
176
+ const rulesJson = JSON.stringify(interceptRules);
177
+ return `export default defineNuxtPlugin({
178
+ name: 'nuxt-scripts:intercept',
179
+ enforce: 'pre',
180
+ setup() {
181
+ const rules = ${rulesJson};
182
+ const origBeacon = typeof navigator !== 'undefined' && navigator.sendBeacon
183
+ ? navigator.sendBeacon.bind(navigator)
184
+ : () => false;
185
+ const origFetch = globalThis.fetch.bind(globalThis);
186
+
187
+ function rewriteUrl(url) {
188
+ try {
189
+ const parsed = new URL(url, location.origin);
190
+ for (const rule of rules) {
191
+ if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
192
+ if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) continue;
193
+ const path = rule.pathPrefix ? parsed.pathname.slice(rule.pathPrefix.length) : parsed.pathname;
194
+ return location.origin + rule.target + (path.startsWith('/') ? '' : '/') + path + parsed.search;
195
+ }
196
+ }
197
+ } catch {}
198
+ return url;
199
+ }
200
+
201
+ // XMLHttpRequest wrapper \u2014 intercepts .open() to rewrite URL
202
+ const OrigXHR = XMLHttpRequest;
203
+ class ProxiedXHR extends OrigXHR {
204
+ open() {
205
+ const args = Array.from(arguments);
206
+ if (typeof args[1] === 'string') args[1] = rewriteUrl(args[1]);
207
+ return super.open.apply(this, args);
244
208
  }
245
- },
246
- hotjar: {
247
- rewrite: [
248
- // Static assets
249
- { from: "static.hotjar.com", to: `${collectPrefix}/hotjar` },
250
- // Script loader (bootstrap script loads the main SDK from here)
251
- { from: "script.hotjar.com", to: `${collectPrefix}/hotjar-script` },
252
- // Configuration/variables
253
- { from: "vars.hotjar.com", to: `${collectPrefix}/hotjar-vars` },
254
- // Data ingestion endpoint
255
- { from: "in.hotjar.com", to: `${collectPrefix}/hotjar-in` },
256
- // Video capture
257
- { from: "vc.hotjar.com", to: `${collectPrefix}/hotjar-vc` },
258
- // Metrics/telemetry
259
- { from: "metrics.hotjar.io", to: `${collectPrefix}/hotjar-metrics` },
260
- // Insights (ContentSquare integration)
261
- { from: "insights.hotjar.com", to: `${collectPrefix}/hotjar-insights` }
262
- ],
263
- routes: {
264
- [`${collectPrefix}/hotjar/**`]: { proxy: "https://static.hotjar.com/**" },
265
- [`${collectPrefix}/hotjar-script/**`]: { proxy: "https://script.hotjar.com/**" },
266
- [`${collectPrefix}/hotjar-vars/**`]: { proxy: "https://vars.hotjar.com/**" },
267
- [`${collectPrefix}/hotjar-in/**`]: { proxy: "https://in.hotjar.com/**" },
268
- [`${collectPrefix}/hotjar-vc/**`]: { proxy: "https://vc.hotjar.com/**" },
269
- [`${collectPrefix}/hotjar-metrics/**`]: { proxy: "https://metrics.hotjar.io/**" },
270
- [`${collectPrefix}/hotjar-insights/**`]: { proxy: "https://insights.hotjar.com/**" }
209
+ }
210
+ // Image wrapper \u2014 intercepts .src setter to rewrite URL
211
+ const OrigImage = Image;
212
+ const origSrcDesc = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, 'src');
213
+ function ProxiedImage(w, h) {
214
+ const img = arguments.length === 2 ? new OrigImage(w, h)
215
+ : arguments.length === 1 ? new OrigImage(w) : new OrigImage();
216
+ if (origSrcDesc && origSrcDesc.set) {
217
+ Object.defineProperty(img, 'src', {
218
+ get() { return origSrcDesc.get.call(this); },
219
+ set(v) { origSrcDesc.set.call(this, typeof v === 'string' ? rewriteUrl(v) : v); },
220
+ configurable: true,
221
+ });
271
222
  }
223
+ return img;
272
224
  }
273
- };
225
+
226
+ globalThis.__nuxtScripts = {
227
+ sendBeacon: (url, data) => origBeacon(rewriteUrl(url), data),
228
+ fetch: (url, opts) => origFetch(typeof url === 'string' ? rewriteUrl(url) : url, opts),
229
+ XMLHttpRequest: ProxiedXHR,
230
+ Image: ProxiedImage,
231
+ };
232
+ },
233
+ })
234
+ `;
274
235
  }
275
- function getProxyConfig(key, collectPrefix) {
276
- const configs = buildProxyConfig(collectPrefix);
277
- return configs[key];
236
+
237
+ function resolveFirstPartyConfig(config) {
238
+ const enabled = !!config.firstParty;
239
+ const prefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
240
+ const collectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
241
+ const privacy = typeof config.firstParty === "object" ? config.firstParty.privacy : void 0;
242
+ const assetsPrefix = prefix || config.assets?.prefix || "/_scripts";
243
+ return { enabled, prefix, collectPrefix, privacy, assetsPrefix };
278
244
  }
279
- function getAllProxyConfigs(collectPrefix) {
280
- return buildProxyConfig(collectPrefix);
245
+ async function setupFirstPartyHandlers(firstParty, resolvePath) {
246
+ const interceptRules = [];
247
+ addPluginTemplate({
248
+ filename: "nuxt-scripts-intercept.client.mjs",
249
+ getContents() {
250
+ return generateInterceptPluginContents(interceptRules);
251
+ }
252
+ });
253
+ const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
254
+ logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstParty.collectPrefix}/**`, "->", proxyHandlerPath);
255
+ addServerHandler({
256
+ route: `${firstParty.collectPrefix}/**`,
257
+ handler: proxyHandlerPath
258
+ });
259
+ return interceptRules;
281
260
  }
282
- function getSWInterceptRules(collectPrefix) {
283
- const configs = buildProxyConfig(collectPrefix);
284
- const rules = [];
285
- for (const config of Object.values(configs)) {
286
- if (!config.routes)
261
+ function finalizeFirstParty(opts) {
262
+ const { firstParty, interceptRules, registryScriptsWithImport, nuxtOptions } = opts;
263
+ const proxyConfigs = getAllProxyConfigs(firstParty.collectPrefix);
264
+ const registryKeys = Object.keys(opts.registry || {});
265
+ const scriptByKey = /* @__PURE__ */ new Map();
266
+ for (const script of registryScriptsWithImport) {
267
+ if (script.registryKey) {
268
+ scriptByKey.set(script.registryKey, script);
269
+ }
270
+ }
271
+ const neededRoutes = {};
272
+ const routePrivacyOverrides = {};
273
+ const unsupportedScripts = [];
274
+ const unmatchedScripts = [];
275
+ for (const key of registryKeys) {
276
+ const script = scriptByKey.get(key) || registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
277
+ if (!script) {
278
+ unmatchedScripts.push(key);
287
279
  continue;
288
- for (const [localPath, { proxy }] of Object.entries(config.routes)) {
289
- const match = proxy.match(/^https?:\/\/([^/]+)(\/.*)?\/\*\*$/);
290
- if (match?.[1]) {
291
- const domain = match[1];
292
- const pathPrefix = match[2] || "";
293
- const target = localPath.replace(/\/\*\*$/, "");
294
- rules.push({ pattern: domain, pathPrefix, target });
280
+ }
281
+ const proxyKey = script?.proxy || void 0;
282
+ if (proxyKey) {
283
+ const proxyConfig = proxyConfigs[proxyKey];
284
+ if (proxyConfig?.routes) {
285
+ Object.assign(neededRoutes, proxyConfig.routes);
286
+ for (const routePath of Object.keys(proxyConfig.routes)) {
287
+ routePrivacyOverrides[routePath] = proxyConfig.privacy;
288
+ }
289
+ } else {
290
+ unsupportedScripts.push(key);
295
291
  }
296
292
  }
297
293
  }
298
- return rules;
294
+ if (opts.registry) {
295
+ autoInjectProxyEndpoints(opts.registry, nuxtOptions.runtimeConfig, firstParty.collectPrefix);
296
+ }
297
+ if (unmatchedScripts.length) {
298
+ logger.warn(
299
+ `First-party mode: could not find registry scripts for: ${unmatchedScripts.join(", ")}.
300
+ These scripts will not have proxy routes registered. Check that the registry key matches a known script.`
301
+ );
302
+ }
303
+ if (unsupportedScripts.length && nuxtOptions.dev) {
304
+ logger.warn(
305
+ `First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
306
+ They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
307
+ );
308
+ }
309
+ interceptRules.push(...routesToInterceptRules(neededRoutes));
310
+ const flatRoutes = {};
311
+ for (const [path, config] of Object.entries(neededRoutes)) {
312
+ flatRoutes[path] = config.proxy;
313
+ }
314
+ nuxtOptions.runtimeConfig["nuxt-scripts-proxy"] = {
315
+ routes: flatRoutes,
316
+ privacy: firstParty.privacy,
317
+ routePrivacy: routePrivacyOverrides
318
+ };
319
+ if (Object.keys(neededRoutes).length && nuxtOptions.dev) {
320
+ const routeCount = Object.keys(neededRoutes).length;
321
+ const scriptsCount = registryKeys.length;
322
+ const privacyLabel = firstParty.privacy === void 0 ? "per-script" : typeof firstParty.privacy === "boolean" ? firstParty.privacy ? "anonymize" : "passthrough" : "custom";
323
+ logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${privacyLabel})`);
324
+ if (logger.level >= 4) {
325
+ for (const [path, config] of Object.entries(neededRoutes)) {
326
+ logger.debug(` ${path} \u2192 ${config.proxy}`);
327
+ }
328
+ }
329
+ }
330
+ const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
331
+ const preset = process.env.NITRO_PRESET || "";
332
+ if (staticPresets.includes(preset)) {
333
+ logger.warn(
334
+ `First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
335
+ Scripts will be bundled, but collection requests will not be proxied.
336
+
337
+ Options:
338
+ 1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
339
+ 2. Switch to server-rendered mode (ssr: true)
340
+ 3. Disable with firstParty: false
341
+
342
+ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
343
+ );
344
+ }
345
+ }
346
+
347
+ const isStackblitz = provider === "stackblitz";
348
+ async function promptToInstall(name, installCommand, options) {
349
+ if (await resolvePackageJSON(name).catch(() => null))
350
+ return true;
351
+ logger$1.info(`Package ${name} is missing`);
352
+ if (isCI)
353
+ return false;
354
+ if (options.prompt === true || options.prompt !== false && !isStackblitz) {
355
+ const confirm = await logger$1.prompt(`Do you want to install ${name} package?`, {
356
+ type: "confirm",
357
+ name: "confirm",
358
+ initial: true
359
+ });
360
+ if (!confirm)
361
+ return false;
362
+ }
363
+ logger$1.info(`Installing ${name}...`);
364
+ try {
365
+ await installCommand();
366
+ logger$1.success(`Installed ${name}`);
367
+ return true;
368
+ } catch (err) {
369
+ logger$1.error(err);
370
+ return false;
371
+ }
372
+ }
373
+ const installPrompts = /* @__PURE__ */ new Set();
374
+ function installNuxtModule(name, options) {
375
+ if (installPrompts.has(name))
376
+ return;
377
+ installPrompts.add(name);
378
+ const nuxt = tryUseNuxt();
379
+ if (!nuxt)
380
+ return;
381
+ return promptToInstall(name, async () => {
382
+ const { runCommand } = await import(String("nuxi"));
383
+ await runCommand("module", ["add", name, "--cwd", nuxt.options.rootDir]);
384
+ }, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
299
385
  }
300
386
 
301
387
  function isVue(id, opts = {}) {
@@ -319,13 +405,301 @@ function isVue(id, opts = {}) {
319
405
  }
320
406
  return true;
321
407
  }
322
- const JS_RE = /\.(?:[cm]?j|t)sx?$/;
408
+ const JS_RE$1 = /\.(?:[cm]?j|t)sx?$/;
323
409
  function isJS(id) {
324
410
  const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
325
- return JS_RE.test(pathname);
411
+ return JS_RE$1.test(pathname);
412
+ }
413
+
414
+ const VUE_RE$1 = /\.vue/;
415
+ function NuxtScriptsCheckScripts() {
416
+ return createUnplugin(() => {
417
+ return {
418
+ name: "nuxt-scripts:check-scripts",
419
+ transform: {
420
+ filter: {
421
+ id: VUE_RE$1
422
+ },
423
+ handler(code, id) {
424
+ if (!isVue(id, { type: ["script"] }))
425
+ return;
426
+ if (!code.includes("useScript"))
427
+ return;
428
+ let nameNode;
429
+ let errorNode;
430
+ parseAndWalk(code, id, (_node) => {
431
+ if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
432
+ const objPattern = _node.declarations[0]?.id;
433
+ for (const property of objPattern.properties) {
434
+ if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === "$script" && property.value.type === "Identifier") {
435
+ nameNode = _node;
436
+ }
437
+ }
438
+ }
439
+ if (nameNode) {
440
+ let sequence = _node.type === "SequenceExpression" ? _node : null;
441
+ let assignmentExpression;
442
+ if (_node.type === "VariableDeclaration") {
443
+ if (_node.declarations[0]?.init?.type === "SequenceExpression") {
444
+ sequence = _node.declarations[0]?.init;
445
+ assignmentExpression = _node.declarations[0]?.init?.expressions?.[0];
446
+ }
447
+ }
448
+ if (sequence && !assignmentExpression) {
449
+ assignmentExpression = sequence.expressions[0]?.type === "AssignmentExpression" ? sequence.expressions[0] : null;
450
+ }
451
+ if (assignmentExpression) {
452
+ const right = assignmentExpression?.right;
453
+ if (right.callee?.name === "_withAsyncContext") {
454
+ if (right.arguments[0]?.body?.name === "$script" || right.arguments[0]?.body?.callee?.object?.name === "$script") {
455
+ errorNode = nameNode;
456
+ }
457
+ }
458
+ }
459
+ }
460
+ });
461
+ if (errorNode) {
462
+ return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
463
+ }
464
+ }
465
+ }
466
+ };
467
+ });
468
+ }
469
+
470
+ const WORD_OR_DOLLAR_RE = /[\w$]/;
471
+ const GA_COLLECT_RE = /([\w$])?"https:\/\/"\+\(.*?\)\+"\.google-analytics\.com\/g\/collect"/g;
472
+ const GA_ANALYTICS_COLLECT_RE = /([\w$])?"https:\/\/"\+\(.*?\)\+"\.analytics\.google\.com\/g\/collect"/g;
473
+ const FATHOM_SELF_HOSTED_RE = /\.src\.indexOf\("cdn\.usefathom\.com"\)\s*<\s*0/;
474
+ function isPropertyKeyAST(parent, ctx) {
475
+ return parent?.type === "Property" && ctx.key === "key" || parent?.type === "SwitchCase" && ctx.key === "test";
476
+ }
477
+ function matchAndRewrite(value, rewrites) {
478
+ for (const { from, to } of rewrites) {
479
+ const isSuffixMatch = from.startsWith(".");
480
+ const fromSlashIdx = from.indexOf("/");
481
+ const fromHost = fromSlashIdx > 0 ? from.slice(0, fromSlashIdx) : from;
482
+ const fromPath = fromSlashIdx > 0 ? from.slice(fromSlashIdx) : "";
483
+ if (!value.includes(fromHost))
484
+ continue;
485
+ const url = parseURL(value);
486
+ let shouldRewrite = false;
487
+ let rewriteSuffix = "";
488
+ if (url.host) {
489
+ const hostMatches = isSuffixMatch ? url.host.endsWith(fromHost) : url.host === fromHost;
490
+ if (hostMatches) {
491
+ const fullPath = url.pathname + (url.search || "") + (url.hash || "");
492
+ if (fromPath && fullPath.startsWith(fromPath)) {
493
+ shouldRewrite = true;
494
+ rewriteSuffix = fullPath.slice(fromPath.length);
495
+ } else if (!fromPath) {
496
+ shouldRewrite = true;
497
+ rewriteSuffix = fullPath;
498
+ }
499
+ }
500
+ } else if (value.startsWith("//")) {
501
+ const hostPart = value.slice(2).split("/")[0];
502
+ const hostMatches = isSuffixMatch ? hostPart?.endsWith(fromHost) ?? false : hostPart === fromHost;
503
+ if (hostMatches) {
504
+ const remainder = value.slice(2 + (hostPart?.length ?? 0));
505
+ if (fromPath && remainder.startsWith(fromPath)) {
506
+ shouldRewrite = true;
507
+ rewriteSuffix = remainder.slice(fromPath.length);
508
+ } else if (!fromPath) {
509
+ shouldRewrite = true;
510
+ rewriteSuffix = remainder;
511
+ }
512
+ }
513
+ } else if (fromPath && (value.startsWith(from) || isSuffixMatch && value.includes(from))) {
514
+ const domainEnd = value.indexOf(from) + from.length;
515
+ const nextChar = value[domainEnd];
516
+ if (!nextChar || nextChar === "/" || nextChar === "?" || nextChar === "#") {
517
+ shouldRewrite = true;
518
+ rewriteSuffix = value.slice(domainEnd);
519
+ }
520
+ }
521
+ if (shouldRewrite) {
522
+ return rewriteSuffix === "/" || rewriteSuffix.startsWith("?") || rewriteSuffix.startsWith("#") ? to + rewriteSuffix : joinURL(to, rewriteSuffix);
523
+ }
524
+ }
525
+ return null;
526
+ }
527
+ const WINDOW_GLOBALS = /* @__PURE__ */ new Set(["window", "self", "globalThis"]);
528
+ const NAVIGATOR_GLOBALS = /* @__PURE__ */ new Set(["navigator"]);
529
+ function resolveToGlobal(name, scopeTracker, depth = 0) {
530
+ if (depth > 10)
531
+ return null;
532
+ const decl = scopeTracker.getDeclaration(name);
533
+ if (!decl)
534
+ return name;
535
+ if (decl instanceof ScopeTrackerFunctionParam)
536
+ return null;
537
+ if (decl instanceof ScopeTrackerVariable) {
538
+ const declarators = decl.variableNode.declarations;
539
+ if (!declarators)
540
+ return null;
541
+ for (const declarator of declarators) {
542
+ const id = declarator.id;
543
+ if (!id || id.name !== name)
544
+ continue;
545
+ const init = declarator.init;
546
+ if (!init)
547
+ return null;
548
+ if (init.type === "Identifier")
549
+ return resolveToGlobal(init.name, scopeTracker, depth + 1);
550
+ if (init.type === "MemberExpression" && !init.computed && init.object?.type === "Identifier" && init.property?.type === "Identifier") {
551
+ const objGlobal = resolveToGlobal(init.object.name, scopeTracker, depth + 1);
552
+ if (!objGlobal)
553
+ return null;
554
+ if (WINDOW_GLOBALS.has(objGlobal) || objGlobal === "document")
555
+ return init.property.name;
556
+ return null;
557
+ }
558
+ return null;
559
+ }
560
+ }
561
+ return null;
562
+ }
563
+ function resolveCalleeTarget(callee, scopeTracker) {
564
+ if (callee?.type !== "MemberExpression")
565
+ return null;
566
+ const propName = callee.computed ? callee.property?.type === "Literal" && typeof callee.property.value === "string" ? callee.property.value : null : callee.property?.name;
567
+ if (!propName)
568
+ return null;
569
+ const obj = callee.object;
570
+ if (!obj || obj.type !== "Identifier")
571
+ return null;
572
+ const resolved = resolveToGlobal(obj.name, scopeTracker);
573
+ if (!resolved)
574
+ return null;
575
+ if (propName === "fetch" && WINDOW_GLOBALS.has(resolved))
576
+ return "fetch";
577
+ if (propName === "sendBeacon" && (NAVIGATOR_GLOBALS.has(resolved) || WINDOW_GLOBALS.has(resolved)))
578
+ return "sendBeacon";
579
+ if (propName === "sendBeacon" && resolved === "navigator")
580
+ return "sendBeacon";
581
+ if (propName === "XMLHttpRequest" && WINDOW_GLOBALS.has(resolved))
582
+ return "XMLHttpRequest";
583
+ if (propName === "Image" && WINDOW_GLOBALS.has(resolved))
584
+ return "Image";
585
+ return null;
586
+ }
587
+ function rewriteScriptUrlsAST(content, filename, rewrites) {
588
+ const s = new MagicString(content);
589
+ function needsLeadingSpace(start) {
590
+ const prev = content[start - 1];
591
+ return prev && WORD_OR_DOLLAR_RE.test(prev) ? " " : "";
592
+ }
593
+ const scopeTracker = new ScopeTracker({ preserveExitedScopes: true });
594
+ const { program } = parseAndWalk(content, filename, { scopeTracker });
595
+ scopeTracker.freeze();
596
+ walk(program, {
597
+ scopeTracker,
598
+ enter(node, parent, ctx) {
599
+ if (node.type === "Literal" && typeof node.value === "string") {
600
+ const value = node.value;
601
+ const rewritten = matchAndRewrite(value, rewrites);
602
+ if (rewritten === null)
603
+ return;
604
+ const quote = content[node.start];
605
+ if (isPropertyKeyAST(parent, ctx)) {
606
+ s.overwrite(node.start, node.end, quote + rewritten + quote);
607
+ } else {
608
+ s.overwrite(node.start, node.end, `${needsLeadingSpace(node.start)}self.location.origin+${quote}${rewritten}${quote}`);
609
+ }
610
+ }
611
+ if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
612
+ const quasis = node.quasis;
613
+ if (quasis?.length === 1) {
614
+ const value = quasis[0].value?.cooked ?? quasis[0].value?.raw;
615
+ if (typeof value !== "string")
616
+ return;
617
+ const rewritten = matchAndRewrite(value, rewrites);
618
+ if (rewritten === null)
619
+ return;
620
+ if (isPropertyKeyAST(parent, ctx)) {
621
+ s.overwrite(node.start, node.end, `\`${rewritten}\``);
622
+ } else {
623
+ s.overwrite(node.start, node.end, `${needsLeadingSpace(node.start)}self.location.origin+\`${rewritten}\``);
624
+ }
625
+ }
626
+ }
627
+ if (node.type === "CallExpression") {
628
+ const callee = node.callee;
629
+ if (callee?.type === "Identifier" && callee.name === "fetch") {
630
+ if (!scopeTracker.getDeclaration("fetch"))
631
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.fetch");
632
+ return;
633
+ }
634
+ const target = resolveCalleeTarget(callee, scopeTracker);
635
+ if (target === "fetch" || target === "sendBeacon") {
636
+ s.overwrite(callee.start, callee.end, `__nuxtScripts.${target}`);
637
+ return;
638
+ }
639
+ if (callee?.type === "MemberExpression" && !callee.computed && callee.property?.name === "sendBeacon" && callee.object?.type === "Identifier") {
640
+ const resolved = resolveToGlobal(callee.object.name, scopeTracker);
641
+ if (resolved === null) {
642
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.sendBeacon");
643
+ }
644
+ }
645
+ }
646
+ if (node.type === "NewExpression") {
647
+ const callee = node.callee;
648
+ if (callee?.type === "Identifier" && callee.name === "XMLHttpRequest") {
649
+ if (!scopeTracker.getDeclaration("XMLHttpRequest"))
650
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.XMLHttpRequest");
651
+ return;
652
+ }
653
+ if (callee?.type === "Identifier" && callee.name === "Image") {
654
+ if (!scopeTracker.getDeclaration("Image"))
655
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.Image");
656
+ return;
657
+ }
658
+ const target = resolveCalleeTarget(callee, scopeTracker);
659
+ if (target === "XMLHttpRequest" || target === "Image") {
660
+ s.overwrite(callee.start, callee.end, `__nuxtScripts.${target}`);
661
+ return;
662
+ }
663
+ if (callee?.type === "MemberExpression" && callee.object?.type === "Identifier") {
664
+ const propName = callee.computed ? callee.property?.type === "Literal" && typeof callee.property.value === "string" ? callee.property.value : null : callee.property?.name;
665
+ if (propName === "XMLHttpRequest" || propName === "Image") {
666
+ const resolved = resolveToGlobal(callee.object.name, scopeTracker);
667
+ if (resolved === null) {
668
+ s.overwrite(callee.start, callee.end, `__nuxtScripts.${propName}`);
669
+ }
670
+ }
671
+ }
672
+ }
673
+ }
674
+ });
675
+ let output = s.toString();
676
+ const gaRewrite = rewrites.find((r) => r.from.includes("google-analytics.com/g/collect"));
677
+ if (gaRewrite) {
678
+ output = output.replace(
679
+ GA_COLLECT_RE,
680
+ (_, prevChar) => `${prevChar ? `${prevChar} ` : ""}self.location.origin+"${gaRewrite.to}"`
681
+ );
682
+ output = output.replace(
683
+ GA_ANALYTICS_COLLECT_RE,
684
+ (_, prevChar) => `${prevChar ? `${prevChar} ` : ""}self.location.origin+"${gaRewrite.to}"`
685
+ );
686
+ }
687
+ if (rewrites.some((r) => r.from === "cdn.usefathom.com")) {
688
+ output = output.replace(
689
+ FATHOM_SELF_HOSTED_RE,
690
+ '.src.indexOf("cdn.usefathom.com")<-1'
691
+ );
692
+ }
693
+ return output;
326
694
  }
327
695
 
328
696
  const SEVEN_DAYS_IN_MS = 7 * 24 * 60 * 60 * 1e3;
697
+ const PROTOCOL_RELATIVE_RE = /^\/\//;
698
+ const VUE_RE = /\.vue/;
699
+ const JS_RE = /\.[cm]?[jt]sx?$/;
700
+ const TEST_RE = /\.(?:test|spec)\./;
701
+ const UPPERCASE_RE = /^[A-Z]$/;
702
+ const USE_SCRIPT_RE = /^useScript/;
329
703
  function calculateIntegrity(content, algorithm = "sha384") {
330
704
  const hash = createHash(algorithm).update(content).digest("base64");
331
705
  return `${algorithm}-${hash}`;
@@ -340,12 +714,10 @@ async function isCacheExpired(storage, filename, cacheMaxAge = SEVEN_DAYS_IN_MS)
340
714
  }
341
715
  function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
342
716
  if (hasProtocol(src, { acceptRelative: true })) {
343
- src = src.replace(/^\/\//, "https://");
717
+ src = src.replace(PROTOCOL_RELATIVE_RE, "https://");
344
718
  const url = parseURL(src);
345
- const file = [
346
- `${hash(url)}.js`
347
- // force an extension
348
- ].filter(Boolean).join("-");
719
+ const h = hash(url);
720
+ const file = `${h.startsWith("-") ? `_${h.slice(1)}` : h}.js`;
349
721
  const nuxt = tryUseNuxt();
350
722
  const cdnURL = nuxt?.options.runtimeConfig?.app?.cdnURL || nuxt?.options.app?.cdnURL || "";
351
723
  const baseURL = cdnURL || nuxt?.options.app.baseURL || "";
@@ -392,7 +764,7 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
392
764
  await storage.setItemRaw(`bundle:${filename}`, res);
393
765
  if (proxyRewrites?.length && res) {
394
766
  const content = res.toString("utf-8");
395
- const rewritten = rewriteScriptUrls(content, proxyRewrites);
767
+ const rewritten = rewriteScriptUrlsAST(content, filename || "script.js", proxyRewrites);
396
768
  res = Buffer.from(rewritten, "utf-8");
397
769
  logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
398
770
  }
@@ -449,8 +821,8 @@ function NuxtScriptBundleTransformer(options = {
449
821
  transform: {
450
822
  filter: {
451
823
  id: {
452
- include: [/\.vue/, /\.[cm]?[jt]sx?$/],
453
- exclude: [/\.(?:test|spec)\./]
824
+ include: [VUE_RE, JS_RE],
825
+ exclude: [TEST_RE]
454
826
  }
455
827
  },
456
828
  async handler(code, id) {
@@ -460,11 +832,11 @@ function NuxtScriptBundleTransformer(options = {
460
832
  return;
461
833
  const s = new MagicString(code);
462
834
  const deferredOps = [];
463
- parseAndWalk(code, id, function(_node) {
835
+ parseAndWalk(code, id, (_node) => {
464
836
  const calleeName = _node.callee?.name;
465
837
  if (!calleeName)
466
838
  return;
467
- const isValidCallee = calleeName === "useScript" || calleeName?.startsWith("useScript") && /^[A-Z]$/.test(calleeName?.charAt(9)) && !calleeName.startsWith("useScriptTrigger") && !calleeName.startsWith("useScriptEvent");
839
+ const isValidCallee = calleeName === "useScript" || calleeName?.startsWith("useScript") && UPPERCASE_RE.test(calleeName?.charAt(9)) && !calleeName.startsWith("useScriptTrigger") && !calleeName.startsWith("useScriptEvent");
468
840
  if (_node.type === "CallExpression" && _node.callee.type === "Identifier" && isValidCallee) {
469
841
  const fnName = _node.callee?.name;
470
842
  const node = _node;
@@ -472,7 +844,7 @@ function NuxtScriptBundleTransformer(options = {
472
844
  let src;
473
845
  let registryKey;
474
846
  if (fnName !== "useScript") {
475
- const baseName = fnName.replace(/^useScript/, "");
847
+ const baseName = fnName.replace(USE_SCRIPT_RE, "");
476
848
  registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : void 0;
477
849
  }
478
850
  if (fnName === "useScript") {
@@ -652,7 +1024,7 @@ function NuxtScriptBundleTransformer(options = {
652
1024
  s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
653
1025
  }
654
1026
  } else {
655
- s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
1027
+ s.overwrite(node.callee.end, node.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
656
1028
  }
657
1029
  }
658
1030
  });
@@ -676,101 +1048,7 @@ function NuxtScriptBundleTransformer(options = {
676
1048
  });
677
1049
  }
678
1050
 
679
- const isStackblitz = provider === "stackblitz";
680
- async function promptToInstall(name, installCommand, options) {
681
- if (await resolvePackageJSON(name).catch(() => null))
682
- return true;
683
- logger$1.info(`Package ${name} is missing`);
684
- if (isCI)
685
- return false;
686
- if (options.prompt === true || options.prompt !== false && !isStackblitz) {
687
- const confirm = await logger$1.prompt(`Do you want to install ${name} package?`, {
688
- type: "confirm",
689
- name: "confirm",
690
- initial: true
691
- });
692
- if (!confirm)
693
- return false;
694
- }
695
- logger$1.info(`Installing ${name}...`);
696
- try {
697
- await installCommand();
698
- logger$1.success(`Installed ${name}`);
699
- return true;
700
- } catch (err) {
701
- logger$1.error(err);
702
- return false;
703
- }
704
- }
705
- const installPrompts = /* @__PURE__ */ new Set();
706
- function installNuxtModule(name, options) {
707
- if (installPrompts.has(name))
708
- return;
709
- installPrompts.add(name);
710
- const nuxt = tryUseNuxt();
711
- if (!nuxt)
712
- return;
713
- return promptToInstall(name, async () => {
714
- const { runCommand } = await import(String("nuxi"));
715
- await runCommand("module", ["add", name, "--cwd", nuxt.options.rootDir]);
716
- }, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
717
- }
718
-
719
- function NuxtScriptsCheckScripts() {
720
- return createUnplugin(() => {
721
- return {
722
- name: "nuxt-scripts:check-scripts",
723
- transform: {
724
- filter: {
725
- id: /\.vue/
726
- },
727
- handler(code, id) {
728
- if (!isVue(id, { type: ["script"] }))
729
- return;
730
- if (!code.includes("useScript"))
731
- return;
732
- let nameNode;
733
- let errorNode;
734
- parseAndWalk(code, id, function(_node) {
735
- if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
736
- const objPattern = _node.declarations[0]?.id;
737
- for (const property of objPattern.properties) {
738
- if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === "$script" && property.value.type === "Identifier") {
739
- nameNode = _node;
740
- }
741
- }
742
- }
743
- if (nameNode) {
744
- let sequence = _node.type === "SequenceExpression" ? _node : null;
745
- let assignmentExpression;
746
- if (_node.type === "VariableDeclaration") {
747
- if (_node.declarations[0]?.init?.type === "SequenceExpression") {
748
- sequence = _node.declarations[0]?.init;
749
- assignmentExpression = _node.declarations[0]?.init?.expressions?.[0];
750
- }
751
- }
752
- if (sequence && !assignmentExpression) {
753
- assignmentExpression = sequence.expressions[0]?.type === "AssignmentExpression" ? sequence.expressions[0] : null;
754
- }
755
- if (assignmentExpression) {
756
- const right = assignmentExpression?.right;
757
- if (right.callee?.name === "_withAsyncContext") {
758
- if (right.arguments[0]?.body?.name === "$script" || right.arguments[0]?.body?.callee?.object?.name === "$script") {
759
- errorNode = nameNode;
760
- }
761
- }
762
- }
763
- }
764
- });
765
- if (errorNode) {
766
- return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
767
- }
768
- }
769
- }
770
- };
771
- });
772
- }
773
-
1051
+ const TRIGGER_PLACEHOLDER_RE = /"__TRIGGER_PLACEHOLDER__"/g;
774
1052
  function registerTypeTemplates({ nuxt, config, newScripts }) {
775
1053
  addTypeTemplate({
776
1054
  filename: "types/nuxt-scripts-augments.d.ts",
@@ -782,7 +1060,7 @@ function registerTypeTemplates({ nuxt, config, newScripts }) {
782
1060
  let augments = `// Generated by @nuxt/scripts
783
1061
  declare module '#app' {
784
1062
  interface NuxtApp {
785
- $scripts: Record<${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`).concat(["string"]).join(" | ")}, import('#nuxt-scripts/types').UseScriptContext<any> | undefined>
1063
+ $scripts: Record<${[...[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`), ...["string"]].join(" | ")}, import('#nuxt-scripts/types').UseScriptContext<any> | undefined>
786
1064
  _scripts: Record<string, import('#nuxt-scripts/types').NuxtDevToolsScriptInstance>
787
1065
  }
788
1066
  interface RuntimeNuxtHooks {
@@ -863,7 +1141,7 @@ function templatePlugin(config, registry) {
863
1141
  let needsInteractionImport = false;
864
1142
  let needsServiceWorkerImport = false;
865
1143
  for (const [k, c] of Object.entries(config.registry || {})) {
866
- const importDefinition = registry.find((i) => i.proxy === k || i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
1144
+ const importDefinition = registry.find((i) => i.import.name.toLowerCase() === `usescript${k.toLowerCase()}`);
867
1145
  if (importDefinition) {
868
1146
  resolvedRegistryKeys.push(k);
869
1147
  imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
@@ -875,12 +1153,15 @@ function templatePlugin(config, registry) {
875
1153
  const triggerResolved = resolveTriggerForTemplate(scriptOptions?.trigger);
876
1154
  if (triggerResolved) {
877
1155
  scriptOptions.trigger = "__TRIGGER_PLACEHOLDER__";
878
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
879
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
880
- if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
1156
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
1157
+ needsIdleTimeoutImport = true;
1158
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
1159
+ needsInteractionImport = true;
1160
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
1161
+ needsServiceWorkerImport = true;
881
1162
  }
882
1163
  const args = { ...input, scriptOptions };
883
- const argsJson = triggerResolved ? JSON.stringify(args).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved) : JSON.stringify(args);
1164
+ const argsJson = triggerResolved ? JSON.stringify(args).replace(TRIGGER_PLACEHOLDER_RE, triggerResolved) : JSON.stringify(args);
884
1165
  inits.push(`const ${k} = ${importDefinition.import.name}(${argsJson})`);
885
1166
  } else {
886
1167
  const args = (typeof c !== "object" ? {} : c) || {};
@@ -895,11 +1176,14 @@ function templatePlugin(config, registry) {
895
1176
  const options = c[1];
896
1177
  const triggerResolved = resolveTriggerForTemplate(options?.trigger);
897
1178
  if (triggerResolved) {
898
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
899
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
900
- if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
1179
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
1180
+ needsIdleTimeoutImport = true;
1181
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
1182
+ needsInteractionImport = true;
1183
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
1184
+ needsServiceWorkerImport = true;
901
1185
  const resolvedOptions = { ...options, trigger: "__TRIGGER_PLACEHOLDER__" };
902
- const optionsJson = JSON.stringify(resolvedOptions).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
1186
+ const optionsJson = JSON.stringify(resolvedOptions).replace(TRIGGER_PLACEHOLDER_RE, triggerResolved);
903
1187
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${optionsJson}, use: () => ({ ${k}: window.${k} }) })`);
904
1188
  } else {
905
1189
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${JSON.stringify(c[1])}, use: () => ({ ${k}: window.${k} }) })`);
@@ -907,11 +1191,14 @@ function templatePlugin(config, registry) {
907
1191
  } else if (typeof c === "object" && c !== null) {
908
1192
  const triggerResolved = resolveTriggerForTemplate(c.trigger);
909
1193
  if (triggerResolved) {
910
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
911
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
912
- if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
1194
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
1195
+ needsIdleTimeoutImport = true;
1196
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
1197
+ needsInteractionImport = true;
1198
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
1199
+ needsServiceWorkerImport = true;
913
1200
  const resolvedOptions = { ...c, trigger: "__TRIGGER_PLACEHOLDER__" };
914
- const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
1201
+ const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(TRIGGER_PLACEHOLDER_RE, triggerResolved);
915
1202
  inits.push(`const ${k} = useScript(${argsJson}, { use: () => ({ ${k}: window.${k} }) })`);
916
1203
  } else {
917
1204
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
@@ -940,12 +1227,93 @@ function templatePlugin(config, registry) {
940
1227
  ` parallel: true,`,
941
1228
  ` setup() {`,
942
1229
  ...inits.map((i) => ` ${i}`),
943
- ` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
1230
+ ` return { provide: { scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
944
1231
  ` }`,
945
1232
  `})`
946
1233
  ].join("\n");
947
1234
  }
948
1235
 
1236
+ const SELF_CLOSING_SCRIPT_RE = /<((?:Script[A-Z]|script-)\w[\w-]*)\b([^>]*?)\/\s*>/g;
1237
+ function fixSelfClosingScriptComponents(nuxt) {
1238
+ function expandTags(content) {
1239
+ SELF_CLOSING_SCRIPT_RE.lastIndex = 0;
1240
+ if (!SELF_CLOSING_SCRIPT_RE.test(content))
1241
+ return null;
1242
+ SELF_CLOSING_SCRIPT_RE.lastIndex = 0;
1243
+ return content.replace(SELF_CLOSING_SCRIPT_RE, (_, tag, attrs) => `<${tag}${attrs.trimEnd()}></${tag}>`);
1244
+ }
1245
+ function fixFile(filePath) {
1246
+ if (!existsSync(filePath))
1247
+ return;
1248
+ const content = readFileSync(filePath, "utf-8");
1249
+ const fixed = expandTags(content);
1250
+ if (fixed)
1251
+ nuxt.vfs[filePath] = fixed;
1252
+ }
1253
+ function scanDir(dir) {
1254
+ if (!existsSync(dir))
1255
+ return;
1256
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1257
+ const fullPath = resolve(dir, entry.name);
1258
+ if (entry.isDirectory())
1259
+ scanDir(fullPath);
1260
+ else if (entry.name.endsWith(".vue"))
1261
+ fixFile(fullPath);
1262
+ }
1263
+ }
1264
+ const pagesDirs = /* @__PURE__ */ new Set();
1265
+ for (const layer of nuxt.options._layers) {
1266
+ pagesDirs.add(resolve(
1267
+ layer.config.srcDir,
1268
+ layer.config.dir?.pages || "pages"
1269
+ ));
1270
+ }
1271
+ for (const dir of pagesDirs) scanDir(dir);
1272
+ if (nuxt.options.dev) {
1273
+ nuxt.hook("builder:watch", (_event, relativePath) => {
1274
+ if (!relativePath.endsWith(".vue"))
1275
+ return;
1276
+ for (const layer of nuxt.options._layers) {
1277
+ const fullPath = resolve(layer.config.srcDir, relativePath);
1278
+ for (const dir of pagesDirs) {
1279
+ if (fullPath.startsWith(`${dir}/`)) {
1280
+ fixFile(fullPath);
1281
+ return;
1282
+ }
1283
+ }
1284
+ }
1285
+ });
1286
+ }
1287
+ }
1288
+ const REGISTRY_ENV_DEFAULTS = {
1289
+ clarity: { id: "" },
1290
+ cloudflareWebAnalytics: { token: "" },
1291
+ crisp: { id: "" },
1292
+ databuddyAnalytics: { clientId: "" },
1293
+ fathomAnalytics: { site: "" },
1294
+ googleAdsense: { client: "" },
1295
+ googleAnalytics: { id: "" },
1296
+ googleMaps: { apiKey: "" },
1297
+ googleRecaptcha: { siteKey: "" },
1298
+ googleSignIn: { clientId: "" },
1299
+ googleTagManager: { id: "" },
1300
+ hotjar: { id: "" },
1301
+ intercom: { app_id: "" },
1302
+ matomoAnalytics: { matomoUrl: "" },
1303
+ metaPixel: { id: "" },
1304
+ paypal: { clientId: "" },
1305
+ plausibleAnalytics: { domain: "" },
1306
+ posthog: { apiKey: "" },
1307
+ redditPixel: { id: "" },
1308
+ rybbitAnalytics: { siteId: "" },
1309
+ segment: { writeKey: "" },
1310
+ snapchatPixel: { id: "" },
1311
+ stripe: {},
1312
+ tiktokPixel: { id: "" },
1313
+ umamiAnalytics: { websiteId: "" },
1314
+ vercelAnalytics: {},
1315
+ xPixel: { id: "" }
1316
+ };
949
1317
  const PARTYTOWN_FORWARDS = {
950
1318
  googleAnalytics: ["dataLayer.push", "gtag"],
951
1319
  plausible: ["plausible"],
@@ -1009,8 +1377,7 @@ const module$1 = defineNuxtModule({
1009
1377
  nuxt.options.runtimeConfig["nuxt-scripts"] = {
1010
1378
  version,
1011
1379
  // Private proxy config with API key (server-side only)
1012
- googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0,
1013
- swTemplate: readFileSync(await resolvePath("./runtime/sw/proxy-sw.template.js"), "utf-8")
1380
+ googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0
1014
1381
  };
1015
1382
  nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
1016
1383
  // expose for devtools
@@ -1021,9 +1388,26 @@ const module$1 = defineNuxtModule({
1021
1388
  };
1022
1389
  if (config.registry) {
1023
1390
  nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
1391
+ const registryWithDefaults = {};
1392
+ for (const [key, value] of Object.entries(config.registry)) {
1393
+ if (value && REGISTRY_ENV_DEFAULTS[key]) {
1394
+ const envDefaults = REGISTRY_ENV_DEFAULTS[key];
1395
+ if (value === true || value === "mock") {
1396
+ registryWithDefaults[key] = { ...envDefaults };
1397
+ } else if (typeof value === "object" && !Array.isArray(value)) {
1398
+ registryWithDefaults[key] = defu(value, envDefaults);
1399
+ } else if (Array.isArray(value)) {
1400
+ registryWithDefaults[key] = defu(value[0] || {}, envDefaults);
1401
+ } else {
1402
+ registryWithDefaults[key] = value;
1403
+ }
1404
+ } else {
1405
+ registryWithDefaults[key] = value;
1406
+ }
1407
+ }
1024
1408
  nuxt.options.runtimeConfig.public.scripts = defu(
1025
1409
  nuxt.options.runtimeConfig.public.scripts || {},
1026
- config.registry
1410
+ registryWithDefaults
1027
1411
  );
1028
1412
  }
1029
1413
  if (config.defaultScriptOptions?.bundle !== void 0) {
@@ -1031,14 +1415,8 @@ const module$1 = defineNuxtModule({
1031
1415
  "`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
1032
1416
  );
1033
1417
  }
1034
- const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
1035
- const preset = process.env.NITRO_PRESET || "";
1036
- const isStaticPreset = staticPresets.includes(preset);
1037
- const firstPartyEnabled = !!config.firstParty;
1038
- const firstPartyPrefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
1039
- const firstPartyCollectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
1040
- const firstPartyPrivacy = typeof config.firstParty === "object" ? config.firstParty.privacy ?? "anonymize" : "anonymize";
1041
- const assetsPrefix = firstPartyPrefix || config.assets?.prefix || "/_scripts";
1418
+ const firstParty = resolveFirstPartyConfig(config);
1419
+ const assetsPrefix = firstParty.assetsPrefix;
1042
1420
  if (config.partytown?.length) {
1043
1421
  config.registry = config.registry || {};
1044
1422
  const requiredForwards = [];
@@ -1090,109 +1468,17 @@ const module$1 = defineNuxtModule({
1090
1468
  path: await resolvePath("./runtime/components"),
1091
1469
  pathPrefix: false
1092
1470
  });
1471
+ fixSelfClosingScriptComponents(nuxt);
1093
1472
  addTemplate({
1094
1473
  filename: "nuxt-scripts-trigger-resolver.mjs",
1095
1474
  getContents() {
1096
1475
  return templateTriggerResolver(config.defaultScriptOptions);
1097
1476
  }
1098
1477
  });
1099
- const swHandlerPath = await resolvePath("./runtime/server/sw-handler");
1100
- logger.debug("[nuxt-scripts] First-party config:", { firstPartyEnabled, firstPartyPrivacy, firstPartyCollectPrefix });
1101
- if (firstPartyEnabled && !nuxt.options.dev) {
1102
- const swPath = "/_nuxt-scripts-sw.js";
1103
- const swRules = getSWInterceptRules(firstPartyCollectPrefix);
1104
- addServerHandler({
1105
- route: swPath,
1106
- handler: swHandlerPath
1107
- });
1108
- addPluginTemplate({
1109
- filename: "nuxt-scripts-sw-register.client.mjs",
1110
- getContents() {
1111
- return `import { defineNuxtPlugin } from 'nuxt/app'
1112
-
1113
- export default defineNuxtPlugin({
1114
- name: 'nuxt-scripts:sw-register',
1115
- enforce: 'pre',
1116
- async setup() {
1117
- if (!('serviceWorker' in navigator)) return;
1118
-
1119
- try {
1120
- const reg = await navigator.serviceWorker.register('${swPath}', { scope: '/' });
1121
-
1122
- // Wait for SW to be active and controlling this page
1123
- if (!navigator.serviceWorker.controller) {
1124
- await new Promise((resolve) => {
1125
- const onControllerChange = () => {
1126
- navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
1127
- resolve();
1128
- };
1129
- navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
1130
-
1131
- // Fallback timeout
1132
- setTimeout(resolve, 2000);
1133
- });
1134
- }
1135
- } catch (err) {
1136
- console.warn('[nuxt-scripts] SW registration failed:', err);
1137
- }
1138
- },
1139
- })
1140
- `;
1141
- }
1142
- });
1143
- addPluginTemplate({
1144
- filename: "nuxt-scripts-beacon-intercept.client.mjs",
1145
- getContents() {
1146
- const rulesJson = JSON.stringify(swRules);
1147
- return `export default defineNuxtPlugin({
1148
- name: 'nuxt-scripts:beacon-intercept',
1149
- enforce: 'pre',
1150
- setup() {
1151
- if (typeof navigator === 'undefined' || !navigator.sendBeacon) return;
1152
-
1153
- const rules = ${rulesJson};
1154
- const originalBeacon = navigator.sendBeacon.bind(navigator);
1155
-
1156
- navigator.sendBeacon = (url, data) => {
1157
- try {
1158
- const parsed = new URL(url, window.location.origin);
1159
-
1160
- // Check if this URL matches any of our proxy rules
1161
- for (const rule of rules) {
1162
- if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
1163
- // Check path prefix if specified
1164
- if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) {
1165
- continue;
1166
- }
1167
-
1168
- // Rewrite to proxy: strip pathPrefix from original, prepend target
1169
- const pathWithoutPrefix = rule.pathPrefix
1170
- ? parsed.pathname.slice(rule.pathPrefix.length)
1171
- : parsed.pathname;
1172
- const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
1173
- const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;
1174
-
1175
- return originalBeacon(proxyUrl, data);
1176
- }
1177
- }
1178
- } catch (e) {
1179
- // URL parsing failed, pass through
1180
- }
1181
-
1182
- return originalBeacon(url, data);
1183
- };
1184
- },
1185
- })
1186
- `;
1187
- }
1188
- });
1189
- nuxt.options.runtimeConfig.public["nuxt-scripts-sw"] = { path: swPath };
1190
- const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
1191
- logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstPartyCollectPrefix}/**`, "->", proxyHandlerPath);
1192
- addServerHandler({
1193
- route: `${firstPartyCollectPrefix}/**`,
1194
- handler: proxyHandlerPath
1195
- });
1478
+ logger.debug("[nuxt-scripts] First-party config:", firstParty);
1479
+ let interceptRules = [];
1480
+ if (firstParty.enabled) {
1481
+ interceptRules = await setupFirstPartyHandlers(firstParty, resolvePath);
1196
1482
  }
1197
1483
  const scripts = await registry(resolvePath);
1198
1484
  for (const script of scripts) {
@@ -1221,74 +1507,14 @@ export default defineNuxtPlugin({
1221
1507
  });
1222
1508
  }
1223
1509
  const { renderedScript } = setupPublicAssetStrategy(config.assets);
1224
- if (firstPartyEnabled) {
1225
- const proxyConfigs = getAllProxyConfigs(firstPartyCollectPrefix);
1226
- const registryKeys = Object.keys(config.registry || {});
1227
- const neededRoutes = {};
1228
- const unsupportedScripts = [];
1229
- for (const key of registryKeys) {
1230
- const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
1231
- const proxyKey = script?.proxy || void 0;
1232
- if (proxyKey) {
1233
- const proxyConfig = proxyConfigs[proxyKey];
1234
- if (proxyConfig?.routes) {
1235
- Object.assign(neededRoutes, proxyConfig.routes);
1236
- } else {
1237
- unsupportedScripts.push(key);
1238
- }
1239
- }
1240
- }
1241
- if (unsupportedScripts.length && nuxt.options.dev) {
1242
- logger.warn(
1243
- `First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
1244
- They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
1245
- );
1246
- }
1247
- const flatRoutes = {};
1248
- for (const [path, config2] of Object.entries(neededRoutes)) {
1249
- flatRoutes[path] = config2.proxy;
1250
- }
1251
- const allRewrites = [];
1252
- for (const key of registryKeys) {
1253
- const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
1254
- const proxyKey = script?.proxy !== false ? script?.proxy || key : void 0;
1255
- if (proxyKey) {
1256
- const proxyConfig = proxyConfigs[proxyKey];
1257
- if (proxyConfig?.rewrite) {
1258
- allRewrites.push(...proxyConfig.rewrite);
1259
- }
1260
- }
1261
- }
1262
- nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
1263
- routes: flatRoutes,
1264
- privacy: firstPartyPrivacy,
1265
- rewrites: allRewrites
1266
- };
1267
- if (Object.keys(neededRoutes).length) {
1268
- if (nuxt.options.dev) {
1269
- const routeCount = Object.keys(neededRoutes).length;
1270
- const scriptsCount = registryKeys.length;
1271
- logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${firstPartyPrivacy})`);
1272
- if (logger.level >= 4) {
1273
- for (const [path, config2] of Object.entries(neededRoutes)) {
1274
- logger.debug(` ${path} \u2192 ${config2.proxy}`);
1275
- }
1276
- }
1277
- }
1278
- }
1279
- if (isStaticPreset) {
1280
- logger.warn(
1281
- `First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
1282
- Scripts will be bundled, but collection requests will not be proxied.
1283
-
1284
- Options:
1285
- 1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
1286
- 2. Switch to server-rendered mode (ssr: true)
1287
- 3. Disable with firstParty: false
1288
-
1289
- See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1290
- );
1291
- }
1510
+ if (firstParty.enabled) {
1511
+ finalizeFirstParty({
1512
+ firstParty,
1513
+ interceptRules,
1514
+ registry: config.registry,
1515
+ registryScriptsWithImport,
1516
+ nuxtOptions: nuxt.options
1517
+ });
1292
1518
  }
1293
1519
  const moduleInstallPromises = /* @__PURE__ */ new Map();
1294
1520
  addBuildPlugin(NuxtScriptsCheckScripts(), {
@@ -1297,9 +1523,9 @@ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1297
1523
  addBuildPlugin(NuxtScriptBundleTransformer({
1298
1524
  scripts: registryScriptsWithImport,
1299
1525
  registryConfig: nuxt.options.runtimeConfig.public.scripts,
1300
- defaultBundle: firstPartyEnabled || config.defaultScriptOptions?.bundle,
1301
- firstPartyEnabled,
1302
- firstPartyCollectPrefix,
1526
+ defaultBundle: firstParty.enabled || config.defaultScriptOptions?.bundle,
1527
+ firstPartyEnabled: firstParty.enabled,
1528
+ firstPartyCollectPrefix: firstParty.collectPrefix,
1303
1529
  moduleDetected(module) {
1304
1530
  if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
1305
1531
  moduleInstallPromises.set(module, () => installNuxtModule(module));
@@ -1312,7 +1538,7 @@ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1312
1538
  renderedScript
1313
1539
  }));
1314
1540
  nuxt.hooks.hook("build:done", async () => {
1315
- const initPromise = Array.from(moduleInstallPromises.values());
1541
+ const initPromise = [...moduleInstallPromises.values()];
1316
1542
  for (const p of initPromise)
1317
1543
  await p?.();
1318
1544
  });
@@ -1323,6 +1549,17 @@ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1323
1549
  handler: await resolvePath("./runtime/server/google-static-maps-proxy")
1324
1550
  });
1325
1551
  }
1552
+ if (config.registry?.gravatar) {
1553
+ const gravatarConfig = typeof config.registry.gravatar === "object" && !Array.isArray(config.registry.gravatar) ? config.registry.gravatar : {};
1554
+ nuxt.options.runtimeConfig.public["nuxt-scripts"] = defu(
1555
+ { gravatarProxy: { cacheMaxAge: gravatarConfig.cacheMaxAge ?? 3600 } },
1556
+ nuxt.options.runtimeConfig.public["nuxt-scripts"]
1557
+ );
1558
+ addServerHandler({
1559
+ route: "/_scripts/gravatar-proxy",
1560
+ handler: await resolvePath("./runtime/server/gravatar-proxy")
1561
+ });
1562
+ }
1326
1563
  addServerHandler({
1327
1564
  route: "/api/_scripts/x-embed",
1328
1565
  handler: await resolvePath("./runtime/server/x-embed")