@nuxt/scripts 0.13.2 → 1.0.0-beta.12

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 (150) hide show
  1. package/README.md +15 -0
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/48AF9EJD.js +1 -0
  5. package/dist/client/_nuxt/Bk6ed9rg.js +1 -0
  6. package/dist/client/_nuxt/C4Cj8gBr.js +162 -0
  7. package/dist/client/_nuxt/{Bje-0OHL.js → DP0kj6Xn.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/919b81d8-ed3a-4222-8a40-df0031cc3b99.json +1 -0
  10. package/dist/client/_nuxt/entry.D45OuV0w.css +1 -0
  11. package/dist/client/_nuxt/error-404.B57D-jUQ.css +1 -0
  12. package/dist/client/_nuxt/error-500.DTHUW7BI.css +1 -0
  13. package/dist/client/index.html +1 -1
  14. package/dist/module.d.mts +113 -4
  15. package/dist/module.d.ts +176 -0
  16. package/dist/module.json +1 -1
  17. package/dist/module.mjs +780 -299
  18. package/dist/registry.d.ts +6 -0
  19. package/dist/registry.mjs +94 -18
  20. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +30 -2
  21. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +40 -15
  22. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +30 -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 +21 -9
  29. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +8 -8
  30. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +21 -9
  31. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue +11 -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.d.vue.ts +1 -1
  36. package/dist/runtime/components/ScriptCrisp.vue +1 -1
  37. package/dist/runtime/components/ScriptCrisp.vue.d.ts +1 -1
  38. package/dist/runtime/components/ScriptGoogleAdsense.vue +1 -1
  39. package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +53 -0
  40. package/dist/runtime/components/ScriptInstagramEmbed.vue +38 -0
  41. package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +53 -0
  42. package/dist/runtime/components/ScriptIntercom.d.vue.ts +1 -1
  43. package/dist/runtime/components/ScriptIntercom.vue +4 -3
  44. package/dist/runtime/components/ScriptIntercom.vue.d.ts +1 -1
  45. package/dist/runtime/components/ScriptPayPalButtons.d.vue.ts +2 -2
  46. package/dist/runtime/components/ScriptPayPalButtons.vue +13 -11
  47. package/dist/runtime/components/ScriptPayPalButtons.vue.d.ts +2 -2
  48. package/dist/runtime/components/ScriptPayPalMarks.d.vue.ts +2 -2
  49. package/dist/runtime/components/ScriptPayPalMarks.vue +10 -8
  50. package/dist/runtime/components/ScriptPayPalMarks.vue.d.ts +2 -2
  51. package/dist/runtime/components/ScriptPayPalMessages.d.vue.ts +2 -2
  52. package/dist/runtime/components/ScriptPayPalMessages.vue +11 -9
  53. package/dist/runtime/components/ScriptPayPalMessages.vue.d.ts +2 -2
  54. package/dist/runtime/components/ScriptStripePricingTable.vue +2 -2
  55. package/dist/runtime/components/ScriptVimeoPlayer.d.vue.ts +2 -2
  56. package/dist/runtime/components/ScriptVimeoPlayer.vue +1 -1
  57. package/dist/runtime/components/ScriptVimeoPlayer.vue.d.ts +2 -2
  58. package/dist/runtime/components/ScriptXEmbed.d.vue.ts +82 -0
  59. package/dist/runtime/components/ScriptXEmbed.vue +76 -0
  60. package/dist/runtime/components/ScriptXEmbed.vue.d.ts +82 -0
  61. package/dist/runtime/components/ScriptYouTubePlayer.d.vue.ts +12 -1
  62. package/dist/runtime/components/ScriptYouTubePlayer.vue +44 -16
  63. package/dist/runtime/components/ScriptYouTubePlayer.vue.d.ts +12 -1
  64. package/dist/runtime/composables/useScript.js +36 -5
  65. package/dist/runtime/composables/useScriptEventPage.js +2 -2
  66. package/dist/runtime/composables/useScriptTriggerConsent.js +1 -1
  67. package/dist/runtime/composables/useScriptTriggerElement.js +1 -1
  68. package/dist/runtime/composables/useScriptTriggerIdleTimeout.js +1 -1
  69. package/dist/runtime/composables/useScriptTriggerServiceWorker.d.ts +7 -0
  70. package/dist/runtime/composables/useScriptTriggerServiceWorker.js +39 -0
  71. package/dist/runtime/npm-script-stub.d.ts +20 -0
  72. package/dist/runtime/npm-script-stub.js +73 -0
  73. package/dist/runtime/registry/clarity.js +21 -25
  74. package/dist/runtime/registry/cloudflare-web-analytics.js +1 -1
  75. package/dist/runtime/registry/crisp.js +1 -1
  76. package/dist/runtime/registry/databuddy-analytics.js +1 -1
  77. package/dist/runtime/registry/fathom-analytics.js +1 -1
  78. package/dist/runtime/registry/google-adsense.js +1 -1
  79. package/dist/runtime/registry/google-analytics.js +2 -2
  80. package/dist/runtime/registry/google-maps.d.ts +1 -1
  81. package/dist/runtime/registry/google-maps.js +1 -1
  82. package/dist/runtime/registry/google-recaptcha.d.ts +27 -0
  83. package/dist/runtime/registry/google-recaptcha.js +45 -0
  84. package/dist/runtime/registry/google-sign-in.d.ts +84 -0
  85. package/dist/runtime/registry/google-sign-in.js +50 -0
  86. package/dist/runtime/registry/google-tag-manager.d.ts +4 -2
  87. package/dist/runtime/registry/google-tag-manager.js +16 -6
  88. package/dist/runtime/registry/hotjar.js +1 -1
  89. package/dist/runtime/registry/instagram-embed.d.ts +23 -0
  90. package/dist/runtime/registry/instagram-embed.js +22 -0
  91. package/dist/runtime/registry/intercom.js +1 -1
  92. package/dist/runtime/registry/lemon-squeezy.d.ts +0 -1
  93. package/dist/runtime/registry/matomo-analytics.js +3 -3
  94. package/dist/runtime/registry/meta-pixel.js +1 -1
  95. package/dist/runtime/registry/npm.js +1 -1
  96. package/dist/runtime/registry/paypal.d.ts +1 -1
  97. package/dist/runtime/registry/paypal.js +2 -2
  98. package/dist/runtime/registry/plausible-analytics.js +21 -13
  99. package/dist/runtime/registry/posthog.d.ts +27 -0
  100. package/dist/runtime/registry/posthog.js +88 -0
  101. package/dist/runtime/registry/reddit-pixel.js +1 -1
  102. package/dist/runtime/registry/rybbit-analytics.js +41 -9
  103. package/dist/runtime/registry/segment.js +1 -1
  104. package/dist/runtime/registry/snapchat-pixel.js +1 -1
  105. package/dist/runtime/registry/stripe.d.ts +1 -1
  106. package/dist/runtime/registry/stripe.js +1 -1
  107. package/dist/runtime/registry/tiktok-pixel.d.ts +44 -0
  108. package/dist/runtime/registry/tiktok-pixel.js +44 -0
  109. package/dist/runtime/registry/umami-analytics.js +1 -1
  110. package/dist/runtime/registry/vimeo-player.d.ts +2 -2
  111. package/dist/runtime/registry/vimeo-player.js +1 -1
  112. package/dist/runtime/registry/x-embed.d.ts +77 -0
  113. package/dist/runtime/registry/x-embed.js +41 -0
  114. package/dist/runtime/registry/x-pixel.js +1 -1
  115. package/dist/runtime/registry/youtube-player.d.ts +7 -7
  116. package/dist/runtime/registry/youtube-player.js +1 -1
  117. package/dist/runtime/server/google-static-maps-proxy.d.ts +2 -0
  118. package/dist/runtime/server/google-static-maps-proxy.js +54 -0
  119. package/dist/runtime/server/instagram-embed-asset.d.ts +2 -0
  120. package/dist/runtime/server/instagram-embed-asset.js +42 -0
  121. package/dist/runtime/server/instagram-embed-image.d.ts +2 -0
  122. package/dist/runtime/server/instagram-embed-image.js +54 -0
  123. package/dist/runtime/server/instagram-embed.d.ts +2 -0
  124. package/dist/runtime/server/instagram-embed.js +91 -0
  125. package/dist/runtime/server/proxy-handler.d.ts +6 -0
  126. package/dist/runtime/server/proxy-handler.js +264 -0
  127. package/dist/runtime/server/utils/privacy.d.ts +141 -0
  128. package/dist/runtime/server/utils/privacy.js +324 -0
  129. package/dist/runtime/server/x-embed-image.d.ts +2 -0
  130. package/dist/runtime/server/x-embed-image.js +53 -0
  131. package/dist/runtime/server/x-embed.d.ts +49 -0
  132. package/dist/runtime/server/x-embed.js +31 -0
  133. package/dist/runtime/types.d.ts +61 -20
  134. package/dist/runtime/utils/pure.d.ts +9 -0
  135. package/dist/runtime/utils/pure.js +0 -0
  136. package/dist/runtime/utils.d.ts +5 -4
  137. package/dist/runtime/utils.js +12 -2
  138. package/dist/shared/scripts.DLRgvHQg.mjs +288 -0
  139. package/dist/stats.d.mts +39 -0
  140. package/dist/stats.d.ts +39 -0
  141. package/dist/stats.mjs +711 -0
  142. package/dist/types.d.mts +1 -1
  143. package/package.json +59 -46
  144. package/dist/client/_nuxt/DMut0W-e.js +0 -162
  145. package/dist/client/_nuxt/builds/meta/5e0206fe-a683-423c-8d59-2596d0b16fee.json +0 -1
  146. package/dist/client/_nuxt/entry.BjfcJo5q.css +0 -1
  147. package/dist/client/_nuxt/error-404.B0ZhSNwd.css +0 -1
  148. package/dist/client/_nuxt/error-500.D4MdgPaC.css +0 -1
  149. package/dist/client/_nuxt/iNmKC7TZ.js +0 -1
  150. package/dist/client/_nuxt/rttsH3SL.js +0 -1
package/dist/module.mjs CHANGED
@@ -1,78 +1,36 @@
1
- import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, tryUseNuxt, logger as logger$1, defineNuxtModule, createResolver, addImports, addComponentsDir, addTemplate, addTypeTemplate, addPluginTemplate, addBuildPlugin, hasNuxtModule } from '@nuxt/kit';
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, extendViteConfig, logger as logger$1, useLogger, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addImports, addComponentsDir, addTemplate, addPluginTemplate, addServerHandler, addBuildPlugin } from '@nuxt/kit';
2
3
  import { defu } from 'defu';
3
- import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
4
- import { existsSync } from 'node:fs';
5
- import fsp from 'node:fs/promises';
6
- import { createUnplugin } from 'unplugin';
7
- import MagicString from 'magic-string';
8
- import { asyncWalk, walk } from 'estree-walker';
9
- import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
10
- import { hash } from 'ohash';
11
4
  import { join, resolve, relative } from 'pathe';
12
- import { colors } from 'consola/utils';
13
- import { fetch, $fetch } from 'ofetch';
5
+ import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
14
6
  import { lazyEventHandler, eventHandler, createError } from 'h3';
7
+ import { fetch, $fetch } from 'ofetch';
8
+ import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
15
9
  import { createStorage } from 'unstorage';
16
10
  import fsDriver from 'unstorage/drivers/fs-lite';
17
- import { pathToFileURL } from 'node:url';
11
+ import { addCustomTab } from '@nuxt/devtools-kit';
18
12
  import { isCI, provider } from 'std-env';
13
+ import { parseAndWalk } from 'oxc-walker';
14
+ import { createUnplugin } from 'unplugin';
15
+ import { pathToFileURL } from 'node:url';
16
+ import { createHash } from 'node:crypto';
17
+ import fsp from 'node:fs/promises';
18
+ import { colors } from 'consola/utils';
19
+ import MagicString from 'magic-string';
20
+ import { hash } from 'ohash';
21
+ import { a as getProxyConfig, g as getAllProxyConfigs, r as routesToInterceptRules } from './shared/scripts.DLRgvHQg.mjs';
19
22
  import { registry } from './registry.mjs';
20
23
 
21
- const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
22
- const DEVTOOLS_UI_LOCAL_PORT = 3300;
23
-
24
- async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
25
- const clientPath = await resolve("./client");
26
- const isProductionBuild = existsSync(clientPath);
27
- if (isProductionBuild) {
28
- nuxt.hook("vite:serverCreated", async (server) => {
29
- const sirv = await import('sirv').then((r) => r.default || r);
30
- server.middlewares.use(
31
- DEVTOOLS_UI_ROUTE,
32
- sirv(clientPath, { dev: true, single: true })
33
- );
34
- });
35
- } else {
36
- extendViteConfig((config) => {
37
- config.server = config.server || {};
38
- config.server.proxy = config.server.proxy || {};
39
- config.server.proxy[DEVTOOLS_UI_ROUTE] = {
40
- target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
41
- changeOrigin: true,
42
- followRedirects: true,
43
- rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
44
- };
45
- });
46
- }
47
- nuxt.hook("devtools:customTabs", (tabs) => {
48
- tabs.push({
49
- // unique identifier
50
- name: "nuxt-scripts",
51
- // title to display in the tab
52
- title: "Scripts",
53
- // any icon from Iconify, or a URL to an image
54
- icon: "carbon:script",
55
- // iframe view
56
- view: {
57
- type: "iframe",
58
- src: DEVTOOLS_UI_ROUTE
59
- }
60
- });
61
- });
62
- }
63
-
64
- const logger = useLogger("@nuxt/scripts");
65
-
66
24
  const renderedScript = /* @__PURE__ */ new Map();
67
25
  const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
68
- const bundleStorage = () => {
26
+ function bundleStorage() {
69
27
  const nuxt = tryUseNuxt();
70
28
  return createStorage({
71
29
  driver: fsDriver({
72
30
  base: resolve(nuxt?.options.rootDir || "", "node_modules/.cache/nuxt/scripts")
73
31
  })
74
32
  });
75
- };
33
+ }
76
34
  function setupPublicAssetStrategy(options = {}) {
77
35
  const assetsBaseURL = options.prefix || "/_scripts";
78
36
  const nuxt = useNuxt();
@@ -85,6 +43,9 @@ function setupPublicAssetStrategy(options = {}) {
85
43
  const scriptDescriptor = renderedScript.get(join(assetsBaseURL, event.path.slice(1)));
86
44
  if (!scriptDescriptor || scriptDescriptor instanceof Error)
87
45
  throw createError({ statusCode: 404 });
46
+ if (scriptDescriptor.content) {
47
+ return scriptDescriptor.content;
48
+ }
88
49
  const key = `bundle:${filename}`;
89
50
  let res = await storage.getItemRaw(key);
90
51
  if (!res) {
@@ -96,31 +57,112 @@ function setupPublicAssetStrategy(options = {}) {
96
57
  })
97
58
  });
98
59
  if (nuxt.options.dev) {
99
- nuxt.options.routeRules ||= {};
100
- nuxt.options.routeRules[joinURL(assetsBaseURL, "**")] = {
60
+ extendRouteRules(joinURL(assetsBaseURL, "**"), {
101
61
  cache: {
102
62
  maxAge: ONE_YEAR_IN_SECONDS
103
63
  }
104
- };
64
+ });
105
65
  }
106
- nuxt.options.nitro.publicAssets ||= [];
107
66
  const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
108
- nuxt.options.nitro.publicAssets.push();
109
- nuxt.options.nitro = defu(nuxt.options.nitro, {
110
- publicAssets: [{
67
+ nuxt.hook("nitro:config", (nitroConfig) => {
68
+ nitroConfig.publicAssets ||= [];
69
+ nitroConfig.publicAssets.push({
111
70
  dir: cacheDir,
112
71
  maxAge: ONE_YEAR_IN_SECONDS,
113
72
  baseURL: assetsBaseURL
114
- }],
115
- prerender: {
116
- ignore: [assetsBaseURL]
117
- }
73
+ });
74
+ nitroConfig.prerender ||= {};
75
+ nitroConfig.prerender.ignore ||= [];
76
+ nitroConfig.prerender.ignore.push(assetsBaseURL);
118
77
  });
119
78
  return {
120
79
  renderedScript
121
80
  };
122
81
  }
123
82
 
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 isStackblitz = provider === "stackblitz";
125
+ async function promptToInstall(name, installCommand, options) {
126
+ if (await resolvePackageJSON(name).catch(() => null))
127
+ return true;
128
+ logger$1.info(`Package ${name} is missing`);
129
+ if (isCI)
130
+ return false;
131
+ if (options.prompt === true || options.prompt !== false && !isStackblitz) {
132
+ const confirm = await logger$1.prompt(`Do you want to install ${name} package?`, {
133
+ type: "confirm",
134
+ name: "confirm",
135
+ initial: true
136
+ });
137
+ if (!confirm)
138
+ return false;
139
+ }
140
+ logger$1.info(`Installing ${name}...`);
141
+ try {
142
+ await installCommand();
143
+ logger$1.success(`Installed ${name}`);
144
+ return true;
145
+ } catch (err) {
146
+ logger$1.error(err);
147
+ return false;
148
+ }
149
+ }
150
+ const installPrompts = /* @__PURE__ */ new Set();
151
+ function installNuxtModule(name, options) {
152
+ if (installPrompts.has(name))
153
+ return;
154
+ installPrompts.add(name);
155
+ const nuxt = tryUseNuxt();
156
+ if (!nuxt)
157
+ return;
158
+ return promptToInstall(name, async () => {
159
+ const { runCommand } = await import(String("nuxi"));
160
+ await runCommand("module", ["add", name, "--cwd", nuxt.options.rootDir]);
161
+ }, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
162
+ }
163
+
164
+ const logger = useLogger("@nuxt/scripts");
165
+
124
166
  function isVue(id, opts = {}) {
125
167
  const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
126
168
  if (id.endsWith(".vue") && !search) {
@@ -148,7 +190,178 @@ function isJS(id) {
148
190
  return JS_RE.test(pathname);
149
191
  }
150
192
 
193
+ function NuxtScriptsCheckScripts() {
194
+ return createUnplugin(() => {
195
+ return {
196
+ name: "nuxt-scripts:check-scripts",
197
+ transform: {
198
+ filter: {
199
+ id: /\.vue/
200
+ },
201
+ handler(code, id) {
202
+ if (!isVue(id, { type: ["script"] }))
203
+ return;
204
+ if (!code.includes("useScript"))
205
+ return;
206
+ let nameNode;
207
+ let errorNode;
208
+ parseAndWalk(code, id, (_node) => {
209
+ if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
210
+ const objPattern = _node.declarations[0]?.id;
211
+ for (const property of objPattern.properties) {
212
+ if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === "$script" && property.value.type === "Identifier") {
213
+ nameNode = _node;
214
+ }
215
+ }
216
+ }
217
+ if (nameNode) {
218
+ let sequence = _node.type === "SequenceExpression" ? _node : null;
219
+ let assignmentExpression;
220
+ if (_node.type === "VariableDeclaration") {
221
+ if (_node.declarations[0]?.init?.type === "SequenceExpression") {
222
+ sequence = _node.declarations[0]?.init;
223
+ assignmentExpression = _node.declarations[0]?.init?.expressions?.[0];
224
+ }
225
+ }
226
+ if (sequence && !assignmentExpression) {
227
+ assignmentExpression = sequence.expressions[0]?.type === "AssignmentExpression" ? sequence.expressions[0] : null;
228
+ }
229
+ if (assignmentExpression) {
230
+ const right = assignmentExpression?.right;
231
+ if (right.callee?.name === "_withAsyncContext") {
232
+ if (right.arguments[0]?.body?.name === "$script" || right.arguments[0]?.body?.callee?.object?.name === "$script") {
233
+ errorNode = nameNode;
234
+ }
235
+ }
236
+ }
237
+ }
238
+ });
239
+ if (errorNode) {
240
+ return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
241
+ }
242
+ }
243
+ }
244
+ };
245
+ });
246
+ }
247
+
248
+ function isPropertyKeyAST(parent, ctx) {
249
+ return parent?.type === "Property" && ctx.key === "key" || parent?.type === "SwitchCase" && ctx.key === "test";
250
+ }
251
+ function matchAndRewrite(value, rewrites) {
252
+ for (const { from, to } of rewrites) {
253
+ const isSuffixMatch = from.startsWith(".");
254
+ const fromSlashIdx = from.indexOf("/");
255
+ const fromHost = fromSlashIdx > 0 ? from.slice(0, fromSlashIdx) : from;
256
+ const fromPath = fromSlashIdx > 0 ? from.slice(fromSlashIdx) : "";
257
+ if (!value.includes(fromHost))
258
+ continue;
259
+ const url = parseURL(value);
260
+ let shouldRewrite = false;
261
+ let rewriteSuffix = "";
262
+ if (url.host) {
263
+ const hostMatches = isSuffixMatch ? url.host.endsWith(fromHost) : url.host === fromHost;
264
+ if (hostMatches) {
265
+ const fullPath = url.pathname + (url.search || "") + (url.hash || "");
266
+ if (fromPath && fullPath.startsWith(fromPath)) {
267
+ shouldRewrite = true;
268
+ rewriteSuffix = fullPath.slice(fromPath.length);
269
+ } else if (!fromPath) {
270
+ shouldRewrite = true;
271
+ rewriteSuffix = fullPath;
272
+ }
273
+ }
274
+ } else if (value.startsWith("//")) {
275
+ const hostPart = value.slice(2).split("/")[0];
276
+ const hostMatches = isSuffixMatch ? hostPart?.endsWith(fromHost) ?? false : hostPart === fromHost;
277
+ if (hostMatches) {
278
+ const remainder = value.slice(2 + (hostPart?.length ?? 0));
279
+ if (fromPath && remainder.startsWith(fromPath)) {
280
+ shouldRewrite = true;
281
+ rewriteSuffix = remainder.slice(fromPath.length);
282
+ } else if (!fromPath) {
283
+ shouldRewrite = true;
284
+ rewriteSuffix = remainder;
285
+ }
286
+ }
287
+ } else if (fromPath && (value.startsWith(from) || isSuffixMatch && value.includes(from))) {
288
+ const domainEnd = value.indexOf(from) + from.length;
289
+ const nextChar = value[domainEnd];
290
+ if (!nextChar || nextChar === "/" || nextChar === "?" || nextChar === "#") {
291
+ shouldRewrite = true;
292
+ rewriteSuffix = value.slice(domainEnd);
293
+ }
294
+ }
295
+ if (shouldRewrite) {
296
+ return rewriteSuffix === "/" || rewriteSuffix.startsWith("?") || rewriteSuffix.startsWith("#") ? to + rewriteSuffix : joinURL(to, rewriteSuffix);
297
+ }
298
+ }
299
+ return null;
300
+ }
301
+ function rewriteScriptUrlsAST(content, filename, rewrites) {
302
+ const s = new MagicString(content);
303
+ parseAndWalk(content, filename, (node, parent, ctx) => {
304
+ if (node.type === "Literal" && typeof node.value === "string") {
305
+ const value = node.value;
306
+ const rewritten = matchAndRewrite(value, rewrites);
307
+ if (rewritten === null)
308
+ return;
309
+ const quote = content[node.start];
310
+ if (isPropertyKeyAST(parent, ctx)) {
311
+ s.overwrite(node.start, node.end, quote + rewritten + quote);
312
+ } else {
313
+ s.overwrite(node.start, node.end, `self.location.origin+${quote}${rewritten}${quote}`);
314
+ }
315
+ }
316
+ if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
317
+ const quasis = node.quasis;
318
+ if (quasis?.length === 1) {
319
+ const value = quasis[0].value?.cooked ?? quasis[0].value?.raw;
320
+ if (typeof value !== "string")
321
+ return;
322
+ const rewritten = matchAndRewrite(value, rewrites);
323
+ if (rewritten === null)
324
+ return;
325
+ if (isPropertyKeyAST(parent, ctx)) {
326
+ s.overwrite(node.start, node.end, `\`${rewritten}\``);
327
+ } else {
328
+ s.overwrite(node.start, node.end, `self.location.origin+\`${rewritten}\``);
329
+ }
330
+ }
331
+ }
332
+ if (node.type === "CallExpression") {
333
+ const callee = node.callee;
334
+ if (callee?.type === "MemberExpression" && !callee.computed && callee.object?.type === "Identifier" && callee.object.name === "navigator" && callee.property?.name === "sendBeacon") {
335
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.sendBeacon");
336
+ }
337
+ if (callee?.type === "Identifier" && callee.name === "fetch") {
338
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.fetch");
339
+ }
340
+ if (callee?.type === "MemberExpression" && !callee.computed && callee.object?.type === "Identifier" && (callee.object.name === "window" || callee.object.name === "self" || callee.object.name === "globalThis") && callee.property?.name === "fetch") {
341
+ s.overwrite(callee.start, callee.end, "__nuxtScripts.fetch");
342
+ }
343
+ }
344
+ });
345
+ let output = s.toString();
346
+ const gaRewrite = rewrites.find((r) => r.from.includes("google-analytics.com/g/collect"));
347
+ if (gaRewrite) {
348
+ output = output.replace(
349
+ /"https:\/\/"\+\(.*?\)\+"\.google-analytics\.com\/g\/collect"/g,
350
+ `self.location.origin+"${gaRewrite.to}"`
351
+ );
352
+ output = output.replace(
353
+ /"https:\/\/"\+\(.*?\)\+"\.analytics\.google\.com\/g\/collect"/g,
354
+ `self.location.origin+"${gaRewrite.to}"`
355
+ );
356
+ }
357
+ return output;
358
+ }
359
+
151
360
  const SEVEN_DAYS_IN_MS = 7 * 24 * 60 * 60 * 1e3;
361
+ function calculateIntegrity(content, algorithm = "sha384") {
362
+ const hash = createHash(algorithm).update(content).digest("base64");
363
+ return `${algorithm}-${hash}`;
364
+ }
152
365
  async function isCacheExpired(storage, filename, cacheMaxAge = SEVEN_DAYS_IN_MS) {
153
366
  const metaKey = `bundle-meta:${filename}`;
154
367
  const meta = await storage.getItem(metaKey);
@@ -161,10 +374,8 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
161
374
  if (hasProtocol(src, { acceptRelative: true })) {
162
375
  src = src.replace(/^\/\//, "https://");
163
376
  const url = parseURL(src);
164
- const file = [
165
- `${hash(url)}.js`
166
- // force an extension
167
- ].filter(Boolean).join("-");
377
+ const h = hash(url);
378
+ const file = `${h.startsWith("-") ? `_${h.slice(1)}` : h}.js`;
168
379
  const nuxt = tryUseNuxt();
169
380
  const cdnURL = nuxt?.options.runtimeConfig?.app?.cdnURL || nuxt?.options.app?.cdnURL || "";
170
381
  const baseURL = cdnURL || nuxt?.options.app.baseURL || "";
@@ -173,7 +384,7 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
173
384
  return { url: src };
174
385
  }
175
386
  async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
176
- const { src, url, filename, forceDownload } = opts;
387
+ const { src, url, filename, forceDownload, integrity, proxyRewrites } = opts;
177
388
  if (src === url || !filename) {
178
389
  return;
179
390
  }
@@ -181,16 +392,19 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
181
392
  const scriptContent = renderedScript.get(src);
182
393
  let res = scriptContent instanceof Error ? void 0 : scriptContent?.content;
183
394
  if (!res) {
184
- const cacheKey = `bundle:${filename}`;
395
+ const proxyRewritesHash = proxyRewrites?.length ? `-${hash(proxyRewrites)}` : "";
396
+ const cacheKey = proxyRewrites?.length ? `bundle-proxy:${filename.replace(".js", `${proxyRewritesHash}.js`)}` : `bundle:${filename}`;
185
397
  const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !await isCacheExpired(storage, filename, cacheMaxAge);
186
398
  if (shouldUseCache) {
187
- const res2 = await storage.getItemRaw(cacheKey);
399
+ const cachedContent = await storage.getItemRaw(cacheKey);
400
+ const meta = await storage.getItem(`bundle-meta:${filename}`);
188
401
  renderedScript.set(url, {
189
- content: res2,
190
- size: res2.length / 1024,
402
+ content: cachedContent,
403
+ size: cachedContent.length / 1024,
191
404
  encoding: "utf-8",
192
405
  src,
193
- filename
406
+ filename,
407
+ integrity: meta?.integrity
194
408
  });
195
409
  return;
196
410
  }
@@ -206,19 +420,29 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
206
420
  return Buffer.from(r._data || await r.arrayBuffer());
207
421
  });
208
422
  await storage.setItemRaw(`bundle:${filename}`, res);
423
+ if (proxyRewrites?.length && res) {
424
+ const content = res.toString("utf-8");
425
+ const rewritten = rewriteScriptUrlsAST(content, filename || "script.js", proxyRewrites);
426
+ res = Buffer.from(rewritten, "utf-8");
427
+ logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
428
+ }
429
+ const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
430
+ await storage.setItemRaw(cacheKey, res);
209
431
  await storage.setItem(`bundle-meta:${filename}`, {
210
432
  timestamp: Date.now(),
211
433
  src,
212
- filename
434
+ filename,
435
+ integrity: integrityHash
213
436
  });
214
437
  size = size || res.length / 1024;
215
- logger.info(`Downloading script ${colors.gray(`${src} \u2192 ${filename} (${size.toFixed(2)} kB ${encoding})`)}`);
438
+ logger.info(`Downloading script ${colors.gray(`${src} \u2192 ${filename} (${size.toFixed(2)} kB ${encoding})${integrityHash ? ` [${integrityHash.slice(0, 15)}...]` : ""}`)}`);
216
439
  renderedScript.set(url, {
217
440
  content: res,
218
441
  size,
219
442
  encoding,
220
443
  src,
221
- filename
444
+ filename,
445
+ integrity: integrityHash
222
446
  });
223
447
  }
224
448
  }
@@ -252,16 +476,21 @@ function NuxtScriptBundleTransformer(options = {
252
476
  return createUnplugin(() => {
253
477
  return {
254
478
  name: "nuxt:scripts:bundler-transformer",
255
- transformInclude(id) {
256
- return isVue(id, { type: ["template", "script"] }) || isJS(id);
257
- },
258
- async transform(code, id) {
259
- if (!code.includes("useScript"))
260
- return;
261
- const ast = this.parse(code);
262
- const s = new MagicString(code);
263
- await asyncWalk(ast, {
264
- async enter(_node) {
479
+ transform: {
480
+ filter: {
481
+ id: {
482
+ include: [/\.vue/, /\.[cm]?[jt]sx?$/],
483
+ exclude: [/\.(?:test|spec)\./]
484
+ }
485
+ },
486
+ async handler(code, id) {
487
+ if (!isVue(id, { type: ["template", "script"] }) && !isJS(id))
488
+ return;
489
+ if (!code.includes("useScript"))
490
+ return;
491
+ const s = new MagicString(code);
492
+ const deferredOps = [];
493
+ parseAndWalk(code, id, (_node) => {
265
494
  const calleeName = _node.callee?.name;
266
495
  if (!calleeName)
267
496
  return;
@@ -271,6 +500,11 @@ function NuxtScriptBundleTransformer(options = {
271
500
  const node = _node;
272
501
  let scriptSrcNode;
273
502
  let src;
503
+ let registryKey;
504
+ if (fnName !== "useScript") {
505
+ const baseName = fnName.replace(/^useScript/, "");
506
+ registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : void 0;
507
+ }
274
508
  if (fnName === "useScript") {
275
509
  if (node.arguments[0]?.type === "Literal") {
276
510
  scriptSrcNode = node.arguments[0];
@@ -287,9 +521,7 @@ function NuxtScriptBundleTransformer(options = {
287
521
  }
288
522
  if (!registryNode.scriptBundling && !registryNode.src)
289
523
  return;
290
- const baseName = fnName.replace(/^useScript/, "");
291
- const registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : "";
292
- const registryConfig = options.registryConfig?.[registryKey] || {};
524
+ const registryConfig = options.registryConfig?.[registryKey || ""] || {};
293
525
  const fnArg0 = {};
294
526
  if (node.arguments[0]?.type === "ObjectExpression") {
295
527
  const optionsNode = node.arguments[0];
@@ -309,6 +541,8 @@ function NuxtScriptBundleTransformer(options = {
309
541
  src = registryNode.scriptBundling && registryNode.scriptBundling(mergedOptions);
310
542
  if (src === false)
311
543
  return;
544
+ if (!src && registryNode.src)
545
+ src = registryNode.src;
312
546
  }
313
547
  }
314
548
  if (!scriptSrcNode && !src) {
@@ -323,8 +557,7 @@ function NuxtScriptBundleTransformer(options = {
323
557
  if (bundleProperty && bundleProperty.value.type === "Literal") {
324
558
  const bundleValue = bundleProperty.value.value;
325
559
  if (bundleValue === true || bundleValue === "force" || String(bundleValue) === "true") {
326
- const valueNode = bundleProperty.value;
327
- s.overwrite(valueNode.start, valueNode.end, `'unsupported'`);
560
+ s.overwrite(bundleProperty.value.start, bundleProperty.value.end, `'unsupported'`);
328
561
  }
329
562
  }
330
563
  }
@@ -341,8 +574,7 @@ function NuxtScriptBundleTransformer(options = {
341
574
  (p) => (p.key?.name === "bundle" || p.key?.value === "bundle") && p.type === "Property"
342
575
  );
343
576
  if (bundleProperty && bundleProperty.value.type === "Literal") {
344
- const value = bundleProperty.value;
345
- const bundleValue = value.value;
577
+ const bundleValue = bundleProperty.value.value;
346
578
  if (bundleValue !== true && bundleValue !== "force" && String(bundleValue) !== "true") {
347
579
  canBundle = false;
348
580
  return;
@@ -370,170 +602,152 @@ function NuxtScriptBundleTransformer(options = {
370
602
  canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
371
603
  forceDownload = bundleValue === "force";
372
604
  }
605
+ const firstPartyOption = scriptOptions?.value.properties?.find((prop) => {
606
+ return prop.type === "Property" && prop.key?.name === "firstParty" && prop.value.type === "Literal";
607
+ });
608
+ let firstPartyOptOut = firstPartyOption?.value.value === false;
609
+ if (!firstPartyOptOut && node.arguments[1]?.type === "ObjectExpression") {
610
+ const secondArgFirstPartyProp = node.arguments[1].properties.find(
611
+ (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
612
+ );
613
+ firstPartyOptOut = secondArgFirstPartyProp?.value.value === false;
614
+ }
615
+ if (!firstPartyOptOut && node.arguments[0]?.type === "ObjectExpression") {
616
+ const firstArgFirstPartyProp = node.arguments[0].properties.find(
617
+ (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
618
+ );
619
+ firstPartyOptOut = firstArgFirstPartyProp?.value.value === false;
620
+ }
373
621
  if (canBundle) {
374
622
  const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL);
375
- let url = _url;
376
- try {
377
- await downloadScript({ src, url, filename, forceDownload }, renderedScript, options.fetchOptions, options.cacheMaxAge);
378
- } catch (e) {
379
- if (options.fallbackOnSrcOnBundleFail) {
380
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
381
- url = src;
382
- } else {
383
- const errorMessage = e?.message || "Unknown error";
384
- if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND")) {
385
- logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
386
- logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
623
+ const script = options.scripts?.find((s2) => s2.import.name === fnName);
624
+ const proxyConfigKey = script?.proxy !== false ? script?.proxy || registryKey : void 0;
625
+ const proxyRewrites = options.firstPartyEnabled && !firstPartyOptOut && proxyConfigKey && options.firstPartyCollectPrefix ? getProxyConfig(proxyConfigKey, options.firstPartyCollectPrefix)?.rewrite : void 0;
626
+ deferredOps.push(async () => {
627
+ let url = _url;
628
+ try {
629
+ await downloadScript({ src, url, filename, forceDownload, proxyRewrites, integrity: options.integrity }, renderedScript, options.fetchOptions, options.cacheMaxAge);
630
+ } catch (e) {
631
+ if (options.fallbackOnSrcOnBundleFail) {
632
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
633
+ url = src;
634
+ } else {
635
+ const errorMessage = e?.message || "Unknown error";
636
+ if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("certificate")) {
637
+ logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
638
+ logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
639
+ }
640
+ throw e;
387
641
  }
388
- throw e;
389
642
  }
390
- }
391
- if (src === url) {
392
- if (src && src.startsWith("/"))
393
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
394
- else
395
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
396
- }
397
- if (scriptSrcNode) {
398
- s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
399
- } else {
400
- if (node.arguments[0]) {
401
- const optionsNode = node.arguments[0];
402
- const scriptInputProperty = optionsNode.properties.find(
403
- (p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
404
- );
405
- if (scriptInputProperty) {
406
- const scriptInput = scriptInputProperty.value;
407
- if (scriptInput.type === "ObjectExpression") {
408
- const srcProperty = scriptInput.properties.find(
409
- (p) => p.key?.name === "src" || p.key?.value === "src"
410
- );
411
- if (srcProperty)
412
- s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
413
- else
414
- s.appendRight(scriptInput.end, `, src: '${url}'`);
415
- }
643
+ if (src === url) {
644
+ if (src && src.startsWith("/"))
645
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
646
+ else
647
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
648
+ }
649
+ const scriptMeta = renderedScript.get(url);
650
+ const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
651
+ if (scriptSrcNode) {
652
+ if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
653
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
654
+ } else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
655
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
656
+ s.appendLeft(node.arguments[0].end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
416
657
  } else {
417
- s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}' }, `);
658
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
418
659
  }
419
660
  } else {
420
- s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}' } })`);
661
+ const integrityProps = integrityHash ? `, integrity: '${integrityHash}', crossorigin: 'anonymous'` : "";
662
+ if (node.arguments[0]) {
663
+ const optionsNode = node.arguments[0];
664
+ const scriptInputProperty = optionsNode.properties.find(
665
+ (p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
666
+ );
667
+ if (scriptInputProperty) {
668
+ const scriptInput = scriptInputProperty.value;
669
+ if (scriptInput.type === "ObjectExpression") {
670
+ const srcProperty = scriptInput.properties.find(
671
+ (p) => p.key?.name === "src" || p.key?.value === "src"
672
+ );
673
+ if (srcProperty) {
674
+ s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
675
+ if (integrityHash)
676
+ s.appendLeft(scriptInput.end - 1, integrityProps);
677
+ } else {
678
+ s.appendRight(scriptInput.end - 1, `, src: '${url}'${integrityProps}`);
679
+ }
680
+ }
681
+ } else {
682
+ s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
683
+ }
684
+ } else {
685
+ s.overwrite(node.callee.end, node.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
686
+ }
421
687
  }
422
- }
688
+ });
423
689
  }
424
690
  }
425
691
  }
426
692
  }
693
+ });
694
+ for (const op of deferredOps) {
695
+ await op();
696
+ }
697
+ if (s.hasChanged()) {
698
+ return {
699
+ code: s.toString(),
700
+ map: s.generateMap({ includeContent: true, source: id })
701
+ };
427
702
  }
428
- });
429
- if (s.hasChanged()) {
430
- return {
431
- code: s.toString(),
432
- map: s.generateMap({ includeContent: true, source: id })
433
- };
434
703
  }
435
704
  }
436
705
  };
437
706
  });
438
707
  }
439
708
 
440
- const isStackblitz = provider === "stackblitz";
441
- async function promptToInstall(name, installCommand, options) {
442
- if (await resolvePackageJSON(name).catch(() => null))
443
- return true;
444
- logger$1.info(`Package ${name} is missing`);
445
- if (isCI)
446
- return false;
447
- if (options.prompt === true || options.prompt !== false && !isStackblitz) {
448
- const confirm = await logger$1.prompt(`Do you want to install ${name} package?`, {
449
- type: "confirm",
450
- name: "confirm",
451
- initial: true
452
- });
453
- if (!confirm)
454
- return false;
709
+ function registerTypeTemplates({ nuxt, config, newScripts }) {
710
+ addTypeTemplate({
711
+ filename: "types/nuxt-scripts-augments.d.ts",
712
+ getContents: () => {
713
+ const typesPath = relative(
714
+ resolve(nuxt.options.rootDir, nuxt.options.buildDir, "types"),
715
+ resolve("runtime/types")
716
+ );
717
+ let augments = `// Generated by @nuxt/scripts
718
+ declare module '#app' {
719
+ interface NuxtApp {
720
+ $scripts: Record<${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`).concat(["string"]).join(" | ")}, import('#nuxt-scripts/types').UseScriptContext<any> | undefined>
721
+ _scripts: Record<string, import('#nuxt-scripts/types').NuxtDevToolsScriptInstance>
455
722
  }
456
- logger$1.info(`Installing ${name}...`);
457
- try {
458
- await installCommand();
459
- logger$1.success(`Installed ${name}`);
460
- return true;
461
- } catch (err) {
462
- logger$1.error(err);
463
- return false;
723
+ interface RuntimeNuxtHooks {
724
+ 'scripts:updated': (ctx: { scripts: Record<string, import('#nuxt-scripts/types').NuxtDevToolsScriptInstance> }) => void | Promise<void>
464
725
  }
465
726
  }
466
- const installPrompts = /* @__PURE__ */ new Set();
467
- function installNuxtModule(name, options) {
468
- if (installPrompts.has(name))
469
- return;
470
- installPrompts.add(name);
471
- const nuxt = tryUseNuxt();
472
- if (!nuxt)
473
- return;
474
- return promptToInstall(name, async () => {
475
- const { runCommand } = await import(String("nuxi"));
476
- await runCommand("module", ["add", name, "--cwd", nuxt.options.rootDir]);
477
- }, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
727
+ `;
728
+ if (newScripts.length) {
729
+ augments += `
730
+ declare module '#nuxt-scripts/types' {
731
+ type _NuxtScriptOptions = Omit<import('${typesPath}').NuxtUseScriptOptions, 'use' | 'beforeInit'>
732
+ interface ScriptRegistry {
733
+ ${newScripts.map((i) => {
734
+ const key = i.import.name.replace("useScript", "");
735
+ const keyLcFirst = key.substring(0, 1).toLowerCase() + key.substring(1);
736
+ return ` ${keyLcFirst}?: import('${i.import.from}').${key}Input | [import('${i.import.from}').${key}Input, _NuxtScriptOptions]`;
737
+ }).join("\n")}
738
+ }
478
739
  }
479
-
480
- function NuxtScriptsCheckScripts() {
481
- return createUnplugin(() => {
482
- return {
483
- name: "nuxt-scripts:check-scripts",
484
- transformInclude(id) {
485
- return isVue(id, { type: ["script"] });
486
- },
487
- async transform(code) {
488
- if (!code.includes("useScript"))
489
- return;
490
- const ast = this.parse(code);
491
- let nameNode;
492
- let errorNode;
493
- walk(ast, {
494
- enter(_node) {
495
- if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
496
- const objPattern = _node.declarations[0]?.id;
497
- for (const property of objPattern.properties) {
498
- if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === "$script" && property.value.type === "Identifier") {
499
- nameNode = _node;
500
- }
501
- }
502
- }
503
- if (nameNode) {
504
- let sequence = _node.type === "SequenceExpression" ? _node : null;
505
- let assignmentExpression;
506
- if (_node.type === "VariableDeclaration") {
507
- if (_node.declarations[0]?.init?.type === "SequenceExpression") {
508
- sequence = _node.declarations[0]?.init;
509
- assignmentExpression = _node.declarations[0]?.init?.expressions?.[0];
510
- }
511
- }
512
- if (sequence && !assignmentExpression) {
513
- assignmentExpression = sequence.expressions[0]?.type === "AssignmentExpression" ? sequence.expressions[0] : null;
514
- }
515
- if (assignmentExpression) {
516
- const right = assignmentExpression?.right;
517
- if (right.callee?.name === "_withAsyncContext") {
518
- if (right.arguments[0]?.body?.name === "$script" || right.arguments[0]?.body?.callee?.object?.name === "$script") {
519
- errorNode = nameNode;
520
- }
521
- }
522
- }
523
- }
524
- }
525
- });
526
- if (errorNode) {
527
- return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
528
- }
740
+ `;
529
741
  }
530
- };
531
- });
742
+ return `${augments}
743
+ export {}`;
744
+ }
745
+ }, { nuxt: true });
532
746
  }
533
-
534
747
  function templateTriggerResolver(defaultScriptOptions) {
535
748
  const needsIdleTimeout = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "idleTimeout" in defaultScriptOptions.trigger;
536
749
  const needsInteraction = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "interaction" in defaultScriptOptions.trigger;
750
+ const needsServiceWorker = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "serviceWorker" in defaultScriptOptions.trigger;
537
751
  const imports = [];
538
752
  if (needsIdleTimeout) {
539
753
  imports.push(`import { useScriptTriggerIdleTimeout } from '#nuxt-scripts/composables/useScriptTriggerIdleTimeout'`);
@@ -541,11 +755,15 @@ function templateTriggerResolver(defaultScriptOptions) {
541
755
  if (needsInteraction) {
542
756
  imports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
543
757
  }
758
+ if (needsServiceWorker) {
759
+ imports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
760
+ }
544
761
  return [
545
762
  ...imports,
546
763
  `export function resolveTrigger(trigger) {`,
547
764
  needsIdleTimeout ? ` if ('idleTimeout' in trigger) return useScriptTriggerIdleTimeout({ timeout: trigger.idleTimeout })` : "",
548
765
  needsInteraction ? ` if ('interaction' in trigger) return useScriptTriggerInteraction({ events: trigger.interaction })` : "",
766
+ needsServiceWorker ? ` if ('serviceWorker' in trigger) return useScriptTriggerServiceWorker()` : "",
549
767
  ` return null`,
550
768
  `}`
551
769
  ].filter(Boolean).join("\n");
@@ -562,6 +780,9 @@ function resolveTriggerForTemplate(trigger) {
562
780
  if ("interaction" in trigger) {
563
781
  return `useScriptTriggerInteraction({ events: ${JSON.stringify(trigger.interaction)} })`;
564
782
  }
783
+ if ("serviceWorker" in trigger) {
784
+ return `useScriptTriggerServiceWorker()`;
785
+ }
565
786
  }
566
787
  return null;
567
788
  }
@@ -572,27 +793,37 @@ function templatePlugin(config, registry) {
572
793
  }
573
794
  const imports = [];
574
795
  const inits = [];
796
+ const resolvedRegistryKeys = [];
575
797
  let needsIdleTimeoutImport = false;
576
798
  let needsInteractionImport = false;
799
+ let needsServiceWorkerImport = false;
577
800
  for (const [k, c] of Object.entries(config.registry || {})) {
578
- const importDefinition = registry.find((i) => i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
801
+ const importDefinition = registry.find((i) => i.import.name.toLowerCase() === `usescript${k.toLowerCase()}`);
579
802
  if (importDefinition) {
803
+ resolvedRegistryKeys.push(k);
580
804
  imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
581
- const args = (typeof c !== "object" ? {} : c) || {};
582
805
  if (c === "mock") {
583
- args.scriptOptions = { trigger: "manual", skipValidation: true };
584
- } else if (Array.isArray(c) && c.length === 2 && c[1]?.trigger) {
585
- const triggerResolved = resolveTriggerForTemplate(c[1].trigger);
806
+ inits.push(`const ${k} = ${importDefinition.import.name}({ scriptOptions: { trigger: 'manual', skipValidation: true } })`);
807
+ } else if (Array.isArray(c) && c.length === 2) {
808
+ const input = c[0] || {};
809
+ const scriptOptions = { ...c[1] };
810
+ const triggerResolved = resolveTriggerForTemplate(scriptOptions?.trigger);
586
811
  if (triggerResolved) {
587
- args.scriptOptions = { ...c[1] };
588
- if (args.scriptOptions) {
589
- args.scriptOptions.trigger = `__TRIGGER_${triggerResolved}__`;
590
- }
591
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
592
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
812
+ scriptOptions.trigger = "__TRIGGER_PLACEHOLDER__";
813
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
814
+ needsIdleTimeoutImport = true;
815
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
816
+ needsInteractionImport = true;
817
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
818
+ needsServiceWorkerImport = true;
593
819
  }
820
+ const args = { ...input, scriptOptions };
821
+ const argsJson = triggerResolved ? JSON.stringify(args).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved) : JSON.stringify(args);
822
+ inits.push(`const ${k} = ${importDefinition.import.name}(${argsJson})`);
823
+ } else {
824
+ const args = (typeof c !== "object" ? {} : c) || {};
825
+ inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args)})`);
594
826
  }
595
- inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args).replace(/"__TRIGGER_(.*?)__"/g, "$1")})`);
596
827
  }
597
828
  }
598
829
  for (const [k, c] of Object.entries(config.globals || {})) {
@@ -602,20 +833,30 @@ function templatePlugin(config, registry) {
602
833
  const options = c[1];
603
834
  const triggerResolved = resolveTriggerForTemplate(options?.trigger);
604
835
  if (triggerResolved) {
605
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
606
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
607
- const resolvedOptions = { ...options, trigger: `__TRIGGER_${triggerResolved}__` };
608
- inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${JSON.stringify(resolvedOptions).replace(/"__TRIGGER_(.*?)__"/g, "$1")}, use: () => ({ ${k}: window.${k} }) })`);
836
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
837
+ needsIdleTimeoutImport = true;
838
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
839
+ needsInteractionImport = true;
840
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
841
+ needsServiceWorkerImport = true;
842
+ const resolvedOptions = { ...options, trigger: "__TRIGGER_PLACEHOLDER__" };
843
+ const optionsJson = JSON.stringify(resolvedOptions).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
844
+ inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${optionsJson}, use: () => ({ ${k}: window.${k} }) })`);
609
845
  } else {
610
846
  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} }) })`);
611
847
  }
612
848
  } else if (typeof c === "object" && c !== null) {
613
849
  const triggerResolved = resolveTriggerForTemplate(c.trigger);
614
850
  if (triggerResolved) {
615
- if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
616
- if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
617
- const resolvedOptions = { ...c, trigger: `__TRIGGER_${triggerResolved}__` };
618
- inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_(.*?)__"/g, "$1")}, { use: () => ({ ${k}: window.${k} }) })`);
851
+ if (triggerResolved.includes("useScriptTriggerIdleTimeout"))
852
+ needsIdleTimeoutImport = true;
853
+ if (triggerResolved.includes("useScriptTriggerInteraction"))
854
+ needsInteractionImport = true;
855
+ if (triggerResolved.includes("useScriptTriggerServiceWorker"))
856
+ needsServiceWorkerImport = true;
857
+ const resolvedOptions = { ...c, trigger: "__TRIGGER_PLACEHOLDER__" };
858
+ const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
859
+ inits.push(`const ${k} = useScript(${argsJson}, { use: () => ({ ${k}: window.${k} }) })`);
619
860
  } else {
620
861
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
621
862
  }
@@ -628,6 +869,9 @@ function templatePlugin(config, registry) {
628
869
  if (needsInteractionImport) {
629
870
  triggerImports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
630
871
  }
872
+ if (needsServiceWorkerImport) {
873
+ triggerImports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
874
+ }
631
875
  return [
632
876
  `import { useScript } from '#nuxt-scripts/composables/useScript'`,
633
877
  `import { defineNuxtPlugin } from 'nuxt/app'`,
@@ -640,12 +884,78 @@ function templatePlugin(config, registry) {
640
884
  ` parallel: true,`,
641
885
  ` setup() {`,
642
886
  ...inits.map((i) => ` ${i}`),
643
- ` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].join(", ")} } } }`,
887
+ ` return { provide: { scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
644
888
  ` }`,
645
889
  `})`
646
890
  ].join("\n");
647
891
  }
648
892
 
893
+ const SELF_CLOSING_SCRIPT_RE = /<((?:Script[A-Z]|script-)\w[\w-]*)\b([^>]*?)\/\s*>/g;
894
+ function fixSelfClosingScriptComponents(nuxt) {
895
+ function expandTags(content) {
896
+ SELF_CLOSING_SCRIPT_RE.lastIndex = 0;
897
+ if (!SELF_CLOSING_SCRIPT_RE.test(content))
898
+ return null;
899
+ SELF_CLOSING_SCRIPT_RE.lastIndex = 0;
900
+ return content.replace(SELF_CLOSING_SCRIPT_RE, (_, tag, attrs) => `<${tag}${attrs.trimEnd()}></${tag}>`);
901
+ }
902
+ function fixFile(filePath) {
903
+ if (!existsSync(filePath))
904
+ return;
905
+ const content = readFileSync(filePath, "utf-8");
906
+ const fixed = expandTags(content);
907
+ if (fixed)
908
+ nuxt.vfs[filePath] = fixed;
909
+ }
910
+ function scanDir(dir) {
911
+ if (!existsSync(dir))
912
+ return;
913
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
914
+ const fullPath = resolve(dir, entry.name);
915
+ if (entry.isDirectory())
916
+ scanDir(fullPath);
917
+ else if (entry.name.endsWith(".vue"))
918
+ fixFile(fullPath);
919
+ }
920
+ }
921
+ const pagesDirs = /* @__PURE__ */ new Set();
922
+ for (const layer of nuxt.options._layers) {
923
+ pagesDirs.add(resolve(
924
+ layer.config.srcDir,
925
+ layer.config.dir?.pages || "pages"
926
+ ));
927
+ }
928
+ for (const dir of pagesDirs) scanDir(dir);
929
+ if (nuxt.options.dev) {
930
+ nuxt.hook("builder:watch", (_event, relativePath) => {
931
+ if (!relativePath.endsWith(".vue"))
932
+ return;
933
+ for (const layer of nuxt.options._layers) {
934
+ const fullPath = resolve(layer.config.srcDir, relativePath);
935
+ for (const dir of pagesDirs) {
936
+ if (fullPath.startsWith(`${dir}/`)) {
937
+ fixFile(fullPath);
938
+ return;
939
+ }
940
+ }
941
+ }
942
+ });
943
+ }
944
+ }
945
+ const PARTYTOWN_FORWARDS = {
946
+ googleAnalytics: ["dataLayer.push", "gtag"],
947
+ plausible: ["plausible"],
948
+ fathom: ["fathom", "fathom.trackEvent", "fathom.trackPageview"],
949
+ umami: ["umami", "umami.track"],
950
+ matomo: ["_paq.push"],
951
+ segment: ["analytics", "analytics.track", "analytics.page", "analytics.identify"],
952
+ metaPixel: ["fbq"],
953
+ xPixel: ["twq"],
954
+ tiktokPixel: ["ttq.track", "ttq.page", "ttq.identify"],
955
+ snapchatPixel: ["snaptr"],
956
+ redditPixel: ["rdt"],
957
+ cloudflareWebAnalytics: ["__cfBeacon"]
958
+ };
649
959
  const module$1 = defineNuxtModule({
650
960
  meta: {
651
961
  name: "@nuxt/scripts",
@@ -655,6 +965,7 @@ const module$1 = defineNuxtModule({
655
965
  }
656
966
  },
657
967
  defaults: {
968
+ firstParty: true,
658
969
  defaultScriptOptions: {
659
970
  trigger: "onNuxtReady"
660
971
  },
@@ -668,6 +979,10 @@ const module$1 = defineNuxtModule({
668
979
  // Configures the maximum time (in milliseconds) allowed for each fetch attempt.
669
980
  }
670
981
  },
982
+ googleStaticMapsProxy: {
983
+ enabled: false,
984
+ cacheMaxAge: 3600
985
+ },
671
986
  enabled: true,
672
987
  debug: false
673
988
  },
@@ -687,11 +1002,17 @@ const module$1 = defineNuxtModule({
687
1002
  if (unheadVersion?.startsWith("1")) {
688
1003
  logger.error(`Nuxt Scripts requires Unhead >= 2, you are using v${unheadVersion}. Please run \`nuxi upgrade --clean\` to upgrade...`);
689
1004
  }
690
- nuxt.options.runtimeConfig["nuxt-scripts"] = { version };
1005
+ nuxt.options.runtimeConfig["nuxt-scripts"] = {
1006
+ version,
1007
+ // Private proxy config with API key (server-side only)
1008
+ googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0
1009
+ };
691
1010
  nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
692
1011
  // expose for devtools
693
1012
  version: nuxt.options.dev ? version : void 0,
694
- defaultScriptOptions: config.defaultScriptOptions
1013
+ defaultScriptOptions: config.defaultScriptOptions,
1014
+ // Only expose enabled and cacheMaxAge to client, not apiKey
1015
+ googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { enabled: true, cacheMaxAge: config.googleStaticMapsProxy.cacheMaxAge } : void 0
695
1016
  };
696
1017
  if (config.registry) {
697
1018
  nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
@@ -700,13 +1021,57 @@ const module$1 = defineNuxtModule({
700
1021
  config.registry
701
1022
  );
702
1023
  }
1024
+ if (config.defaultScriptOptions?.bundle !== void 0) {
1025
+ logger.warn(
1026
+ "`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
1027
+ );
1028
+ }
1029
+ const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
1030
+ const preset = process.env.NITRO_PRESET || "";
1031
+ const isStaticPreset = staticPresets.includes(preset);
1032
+ const firstPartyEnabled = !!config.firstParty;
1033
+ const firstPartyPrefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
1034
+ const firstPartyCollectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
1035
+ const firstPartyPrivacy = typeof config.firstParty === "object" ? config.firstParty.privacy : void 0;
1036
+ const assetsPrefix = firstPartyPrefix || config.assets?.prefix || "/_scripts";
1037
+ if (config.partytown?.length) {
1038
+ config.registry = config.registry || {};
1039
+ const requiredForwards = [];
1040
+ for (const scriptKey of config.partytown) {
1041
+ const forwards = PARTYTOWN_FORWARDS[scriptKey];
1042
+ if (forwards) {
1043
+ requiredForwards.push(...forwards);
1044
+ } else if (import.meta.dev) {
1045
+ logger.warn(`[partytown] "${scriptKey}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
1046
+ }
1047
+ const reg = config.registry;
1048
+ const existing = reg[scriptKey];
1049
+ if (Array.isArray(existing)) {
1050
+ existing[1] = { ...existing[1], partytown: true };
1051
+ } else if (existing && typeof existing === "object" && existing !== true && existing !== "mock") {
1052
+ reg[scriptKey] = [existing, { partytown: true }];
1053
+ } else if (existing === true || existing === "mock") {
1054
+ reg[scriptKey] = [{}, { partytown: true }];
1055
+ } else {
1056
+ reg[scriptKey] = [{}, { partytown: true }];
1057
+ }
1058
+ }
1059
+ if (requiredForwards.length && hasNuxtModule("@nuxtjs/partytown")) {
1060
+ const partytownConfig = nuxt.options.partytown || {};
1061
+ const existingForwards = partytownConfig.forward || [];
1062
+ const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...requiredForwards])];
1063
+ nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
1064
+ logger.info(`[partytown] Auto-configured forwards: ${requiredForwards.join(", ")}`);
1065
+ }
1066
+ }
703
1067
  const composables = [
704
1068
  "useScript",
705
1069
  "useScriptEventPage",
706
1070
  "useScriptTriggerConsent",
707
1071
  "useScriptTriggerElement",
708
1072
  "useScriptTriggerIdleTimeout",
709
- "useScriptTriggerInteraction"
1073
+ "useScriptTriggerInteraction",
1074
+ "useScriptTriggerServiceWorker"
710
1075
  ];
711
1076
  for (const composable of composables) {
712
1077
  addImports({
@@ -720,12 +1085,60 @@ const module$1 = defineNuxtModule({
720
1085
  path: await resolvePath("./runtime/components"),
721
1086
  pathPrefix: false
722
1087
  });
1088
+ fixSelfClosingScriptComponents(nuxt);
723
1089
  addTemplate({
724
1090
  filename: "nuxt-scripts-trigger-resolver.mjs",
725
1091
  getContents() {
726
1092
  return templateTriggerResolver(config.defaultScriptOptions);
727
1093
  }
728
1094
  });
1095
+ logger.debug("[nuxt-scripts] First-party config:", { firstPartyEnabled, firstPartyPrivacy, firstPartyCollectPrefix });
1096
+ let interceptRules = [];
1097
+ if (firstPartyEnabled) {
1098
+ addPluginTemplate({
1099
+ filename: "nuxt-scripts-intercept.client.mjs",
1100
+ getContents() {
1101
+ const rulesJson = JSON.stringify(interceptRules);
1102
+ return `export default defineNuxtPlugin({
1103
+ name: 'nuxt-scripts:intercept',
1104
+ enforce: 'pre',
1105
+ setup() {
1106
+ const rules = ${rulesJson};
1107
+ const origBeacon = typeof navigator !== 'undefined' && navigator.sendBeacon
1108
+ ? navigator.sendBeacon.bind(navigator)
1109
+ : () => false;
1110
+ const origFetch = globalThis.fetch.bind(globalThis);
1111
+
1112
+ function rewriteUrl(url) {
1113
+ try {
1114
+ const parsed = new URL(url, location.origin);
1115
+ for (const rule of rules) {
1116
+ if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
1117
+ if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) continue;
1118
+ const path = rule.pathPrefix ? parsed.pathname.slice(rule.pathPrefix.length) : parsed.pathname;
1119
+ return location.origin + rule.target + (path.startsWith('/') ? '' : '/') + path + parsed.search;
1120
+ }
1121
+ }
1122
+ } catch {}
1123
+ return url;
1124
+ }
1125
+
1126
+ globalThis.__nuxtScripts = {
1127
+ sendBeacon: (url, data) => origBeacon(rewriteUrl(url), data),
1128
+ fetch: (url, opts) => origFetch(typeof url === 'string' ? rewriteUrl(url) : url, opts),
1129
+ };
1130
+ },
1131
+ })
1132
+ `;
1133
+ }
1134
+ });
1135
+ const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
1136
+ logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstPartyCollectPrefix}/**`, "->", proxyHandlerPath);
1137
+ addServerHandler({
1138
+ route: `${firstPartyCollectPrefix}/**`,
1139
+ handler: proxyHandlerPath
1140
+ });
1141
+ }
729
1142
  const scripts = await registry(resolvePath);
730
1143
  for (const script of scripts) {
731
1144
  if (script.import?.name) {
@@ -743,42 +1156,7 @@ const module$1 = defineNuxtModule({
743
1156
  }
744
1157
  const registryScriptsWithImport = registryScripts.filter((i) => !!i.import?.name);
745
1158
  const newScripts = registryScriptsWithImport.filter((i) => !scripts.some((r) => r.import?.name === i.import.name));
746
- addTypeTemplate({
747
- filename: "module/nuxt-scripts.d.ts",
748
- getContents: (data) => {
749
- const typesPath = relative(resolve(data.nuxt.options.rootDir, data.nuxt.options.buildDir, "module"), resolve("runtime/types"));
750
- let types = `
751
- declare module '#app' {
752
- interface NuxtApp {
753
- $scripts: Record<${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`).concat(["string"]).join(" | ")}, (import('#nuxt-scripts/types').UseScriptContext<any>)>
754
- _scripts: Record<string, (import('#nuxt-scripts/types').UseScriptContext<any>)>
755
- }
756
- interface RuntimeNuxtHooks {
757
- 'scripts:updated': (ctx: { scripts: Record<string, (import('#nuxt-scripts/types').UseScriptContext<any>)> }) => void | Promise<void>
758
- }
759
- }
760
- `;
761
- if (newScripts.length) {
762
- types = `${types}
763
- declare module '#nuxt-scripts/types' {
764
- type NuxtUseScriptOptions = Omit<import('${typesPath}').NuxtUseScriptOptions, 'use' | 'beforeInit'>
765
- interface ScriptRegistry {
766
- ${newScripts.map((i) => {
767
- const key = i.import?.name.replace("useScript", "");
768
- const keyLcFirst = key.substring(0, 1).toLowerCase() + key.substring(1);
769
- return ` ${keyLcFirst}?: import('${i.import?.from}').${key}Input | [import('${i.import?.from}').${key}Input, NuxtUseScriptOptions]`;
770
- }).join("\n")}
771
- }
772
- }`;
773
- return types;
774
- }
775
- return `${types}
776
- export {}`;
777
- }
778
- }, {
779
- nuxt: true,
780
- node: true
781
- });
1159
+ registerTypeTemplates({ nuxt, config, newScripts });
782
1160
  if (Object.keys(config.globals || {}).length || Object.keys(config.registry || {}).length) {
783
1161
  addPluginTemplate({
784
1162
  filename: `modules/${name.replace("/", "-")}/plugin.mjs`,
@@ -788,6 +1166,79 @@ export {}`;
788
1166
  });
789
1167
  }
790
1168
  const { renderedScript } = setupPublicAssetStrategy(config.assets);
1169
+ if (firstPartyEnabled) {
1170
+ const proxyConfigs = getAllProxyConfigs(firstPartyCollectPrefix);
1171
+ const registryKeys = Object.keys(config.registry || {});
1172
+ const neededRoutes = {};
1173
+ const routePrivacyOverrides = {};
1174
+ const unsupportedScripts = [];
1175
+ for (const key of registryKeys) {
1176
+ const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
1177
+ const proxyKey = script?.proxy || void 0;
1178
+ if (proxyKey) {
1179
+ const proxyConfig = proxyConfigs[proxyKey];
1180
+ if (proxyConfig?.routes) {
1181
+ Object.assign(neededRoutes, proxyConfig.routes);
1182
+ for (const routePath of Object.keys(proxyConfig.routes)) {
1183
+ routePrivacyOverrides[routePath] = proxyConfig.privacy;
1184
+ }
1185
+ } else {
1186
+ unsupportedScripts.push(key);
1187
+ }
1188
+ }
1189
+ }
1190
+ if (config.registry?.posthog && typeof config.registry.posthog === "object") {
1191
+ const phConfig = config.registry.posthog;
1192
+ if (!phConfig.apiHost) {
1193
+ const region = phConfig.region || "us";
1194
+ phConfig.apiHost = region === "eu" ? `${firstPartyCollectPrefix}/ph-eu` : `${firstPartyCollectPrefix}/ph`;
1195
+ }
1196
+ }
1197
+ if (unsupportedScripts.length && nuxt.options.dev) {
1198
+ logger.warn(
1199
+ `First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
1200
+ They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
1201
+ );
1202
+ }
1203
+ interceptRules = routesToInterceptRules(neededRoutes);
1204
+ const flatRoutes = {};
1205
+ for (const [path, config2] of Object.entries(neededRoutes)) {
1206
+ flatRoutes[path] = config2.proxy;
1207
+ }
1208
+ nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
1209
+ routes: flatRoutes,
1210
+ privacy: firstPartyPrivacy,
1211
+ // undefined = use per-script defaults, set = global override
1212
+ routePrivacy: routePrivacyOverrides
1213
+ // per-script privacy from registry
1214
+ };
1215
+ if (Object.keys(neededRoutes).length) {
1216
+ if (nuxt.options.dev) {
1217
+ const routeCount = Object.keys(neededRoutes).length;
1218
+ const scriptsCount = registryKeys.length;
1219
+ const privacyLabel = firstPartyPrivacy === void 0 ? "per-script" : typeof firstPartyPrivacy === "boolean" ? firstPartyPrivacy ? "anonymize" : "passthrough" : "custom";
1220
+ logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${privacyLabel})`);
1221
+ if (logger.level >= 4) {
1222
+ for (const [path, config2] of Object.entries(neededRoutes)) {
1223
+ logger.debug(` ${path} \u2192 ${config2.proxy}`);
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+ if (isStaticPreset) {
1229
+ logger.warn(
1230
+ `First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
1231
+ Scripts will be bundled, but collection requests will not be proxied.
1232
+
1233
+ Options:
1234
+ 1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
1235
+ 2. Switch to server-rendered mode (ssr: true)
1236
+ 3. Disable with firstParty: false
1237
+
1238
+ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1239
+ );
1240
+ }
1241
+ }
791
1242
  const moduleInstallPromises = /* @__PURE__ */ new Map();
792
1243
  addBuildPlugin(NuxtScriptsCheckScripts(), {
793
1244
  dev: true
@@ -795,15 +1246,18 @@ export {}`;
795
1246
  addBuildPlugin(NuxtScriptBundleTransformer({
796
1247
  scripts: registryScriptsWithImport,
797
1248
  registryConfig: nuxt.options.runtimeConfig.public.scripts,
798
- defaultBundle: config.defaultScriptOptions?.bundle,
1249
+ defaultBundle: firstPartyEnabled || config.defaultScriptOptions?.bundle,
1250
+ firstPartyEnabled,
1251
+ firstPartyCollectPrefix,
799
1252
  moduleDetected(module) {
800
1253
  if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
801
1254
  moduleInstallPromises.set(module, () => installNuxtModule(module));
802
1255
  },
803
- assetsBaseURL: config.assets?.prefix,
1256
+ assetsBaseURL: assetsPrefix,
804
1257
  fallbackOnSrcOnBundleFail: config.assets?.fallbackOnSrcOnBundleFail,
805
1258
  fetchOptions: config.assets?.fetchOptions,
806
1259
  cacheMaxAge: config.assets?.cacheMaxAge,
1260
+ integrity: config.assets?.integrity,
807
1261
  renderedScript
808
1262
  }));
809
1263
  nuxt.hooks.hook("build:done", async () => {
@@ -812,8 +1266,35 @@ export {}`;
812
1266
  await p?.();
813
1267
  });
814
1268
  });
815
- if (nuxt.options.dev)
1269
+ if (config.googleStaticMapsProxy?.enabled) {
1270
+ addServerHandler({
1271
+ route: "/_scripts/google-static-maps-proxy",
1272
+ handler: await resolvePath("./runtime/server/google-static-maps-proxy")
1273
+ });
1274
+ }
1275
+ addServerHandler({
1276
+ route: "/api/_scripts/x-embed",
1277
+ handler: await resolvePath("./runtime/server/x-embed")
1278
+ });
1279
+ addServerHandler({
1280
+ route: "/api/_scripts/x-embed-image",
1281
+ handler: await resolvePath("./runtime/server/x-embed-image")
1282
+ });
1283
+ addServerHandler({
1284
+ route: "/api/_scripts/instagram-embed",
1285
+ handler: await resolvePath("./runtime/server/instagram-embed")
1286
+ });
1287
+ addServerHandler({
1288
+ route: "/api/_scripts/instagram-embed-image",
1289
+ handler: await resolvePath("./runtime/server/instagram-embed-image")
1290
+ });
1291
+ addServerHandler({
1292
+ route: "/api/_scripts/instagram-embed-asset",
1293
+ handler: await resolvePath("./runtime/server/instagram-embed-asset")
1294
+ });
1295
+ if (nuxt.options.dev) {
816
1296
  setupDevToolsUI(config, resolvePath);
1297
+ }
817
1298
  }
818
1299
  });
819
1300