@nuxt/scripts 1.0.0-beta.1 → 1.0.0-beta.3
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.
- package/README.md +6 -0
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/CD5B-xvT.js +1 -0
- package/dist/client/_nuxt/D-kOnTuH.js +162 -0
- package/dist/client/_nuxt/DdVDSbUA.js +1 -0
- package/dist/client/_nuxt/{DTDyDxvR.js → Ds2G8aQM.js} +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/f1474569-6922-450d-bc3f-4fd5f3e1391a.json +1 -0
- package/dist/client/_nuxt/entry.D45OuV0w.css +1 -0
- package/dist/client/_nuxt/error-404.B57D-jUQ.css +1 -0
- package/dist/client/_nuxt/error-500.DTHUW7BI.css +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.d.mts +87 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +678 -142
- package/dist/registry.mjs +11 -0
- package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +53 -0
- package/dist/runtime/components/ScriptInstagramEmbed.vue +38 -0
- package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +53 -0
- package/dist/runtime/components/ScriptXEmbed.d.vue.ts +82 -0
- package/dist/runtime/components/ScriptXEmbed.vue +76 -0
- package/dist/runtime/components/ScriptXEmbed.vue.d.ts +82 -0
- package/dist/runtime/composables/useScript.js +24 -3
- package/dist/runtime/composables/useScriptTriggerServiceWorker.d.ts +7 -0
- package/dist/runtime/composables/useScriptTriggerServiceWorker.js +39 -0
- package/dist/runtime/plugins/sw-register.client.d.ts +2 -0
- package/dist/runtime/plugins/sw-register.client.js +12 -0
- package/dist/runtime/registry/instagram-embed.d.ts +23 -0
- package/dist/runtime/registry/instagram-embed.js +22 -0
- package/dist/runtime/registry/lemon-squeezy.d.ts +0 -1
- package/dist/runtime/registry/plausible-analytics.js +2 -2
- package/dist/runtime/registry/posthog.d.ts +1 -0
- package/dist/runtime/registry/posthog.js +2 -8
- package/dist/runtime/registry/tiktok-pixel.d.ts +1 -0
- package/dist/runtime/registry/tiktok-pixel.js +1 -0
- package/dist/runtime/registry/x-embed.d.ts +77 -0
- package/dist/runtime/registry/x-embed.js +41 -0
- package/dist/runtime/server/instagram-embed-asset.d.ts +2 -0
- package/dist/runtime/server/instagram-embed-asset.js +42 -0
- package/dist/runtime/server/instagram-embed-image.d.ts +2 -0
- package/dist/runtime/server/instagram-embed-image.js +54 -0
- package/dist/runtime/server/instagram-embed.d.ts +2 -0
- package/dist/runtime/server/instagram-embed.js +91 -0
- package/dist/runtime/server/proxy-handler.d.ts +6 -0
- package/dist/runtime/server/proxy-handler.js +246 -0
- package/dist/runtime/server/sw-handler.d.ts +2 -0
- package/dist/runtime/server/sw-handler.js +25 -0
- package/dist/runtime/server/utils/privacy.d.ts +141 -0
- package/dist/runtime/server/utils/privacy.js +309 -0
- package/dist/runtime/server/x-embed-image.d.ts +2 -0
- package/dist/runtime/server/x-embed-image.js +53 -0
- package/dist/runtime/server/x-embed.d.ts +49 -0
- package/dist/runtime/server/x-embed.js +31 -0
- package/dist/runtime/sw/proxy-sw.template.d.ts +1 -0
- package/dist/runtime/sw/proxy-sw.template.js +54 -0
- package/dist/runtime/types.d.ts +29 -0
- package/dist/runtime/utils/pure.d.ts +13 -0
- package/dist/runtime/utils/pure.js +67 -0
- package/dist/runtime/utils.d.ts +1 -1
- package/dist/runtime/utils.js +2 -1
- package/dist/types.d.mts +1 -1
- package/package.json +27 -26
- package/dist/client/_nuxt/Bdf7Qtwg.js +0 -1
- package/dist/client/_nuxt/CoyZWCgl.js +0 -162
- package/dist/client/_nuxt/Ds1k3yKJ.js +0 -1
- package/dist/client/_nuxt/builds/meta/62574f80-71d4-4f9e-8b96-145c85230d99.json +0 -1
- package/dist/client/_nuxt/entry.BjfcJo5q.css +0 -1
- package/dist/client/_nuxt/error-404.D45Vtjcx.css +0 -1
- package/dist/client/_nuxt/error-500.BOm1rWQf.css +0 -1
package/dist/module.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, tryUseNuxt, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, addImports, addComponentsDir, addTemplate, addPluginTemplate, addBuildPlugin
|
|
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';
|
|
2
3
|
import { defu } from 'defu';
|
|
3
4
|
import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
|
|
4
|
-
import {
|
|
5
|
+
import { addCustomTab } from '@nuxt/devtools-kit';
|
|
5
6
|
import { createHash } from 'node:crypto';
|
|
6
7
|
import fsp from 'node:fs/promises';
|
|
7
8
|
import { createUnplugin } from 'unplugin';
|
|
8
9
|
import MagicString from 'magic-string';
|
|
9
|
-
import {
|
|
10
|
+
import { parseAndWalk } from 'oxc-walker';
|
|
10
11
|
import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
|
|
11
12
|
import { hash } from 'ohash';
|
|
12
13
|
import { join, resolve, relative } from 'pathe';
|
|
@@ -15,6 +16,7 @@ import { fetch, $fetch } from 'ofetch';
|
|
|
15
16
|
import { lazyEventHandler, eventHandler, createError } from 'h3';
|
|
16
17
|
import { createStorage } from 'unstorage';
|
|
17
18
|
import fsDriver from 'unstorage/drivers/fs-lite';
|
|
19
|
+
import { rewriteScriptUrls } from '../dist/runtime/utils/pure.js';
|
|
18
20
|
import { pathToFileURL } from 'node:url';
|
|
19
21
|
import { isCI, provider } from 'std-env';
|
|
20
22
|
import { registry } from './registry.mjs';
|
|
@@ -45,20 +47,18 @@ async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
|
|
|
45
47
|
};
|
|
46
48
|
});
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
});
|
|
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
62
|
});
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -86,6 +86,9 @@ function setupPublicAssetStrategy(options = {}) {
|
|
|
86
86
|
const scriptDescriptor = renderedScript.get(join(assetsBaseURL, event.path.slice(1)));
|
|
87
87
|
if (!scriptDescriptor || scriptDescriptor instanceof Error)
|
|
88
88
|
throw createError({ statusCode: 404 });
|
|
89
|
+
if (scriptDescriptor.content) {
|
|
90
|
+
return scriptDescriptor.content;
|
|
91
|
+
}
|
|
89
92
|
const key = `bundle:${filename}`;
|
|
90
93
|
let res = await storage.getItemRaw(key);
|
|
91
94
|
if (!res) {
|
|
@@ -97,31 +100,237 @@ function setupPublicAssetStrategy(options = {}) {
|
|
|
97
100
|
})
|
|
98
101
|
});
|
|
99
102
|
if (nuxt.options.dev) {
|
|
100
|
-
|
|
101
|
-
nuxt.options.routeRules[joinURL(assetsBaseURL, "**")] = {
|
|
103
|
+
extendRouteRules(joinURL(assetsBaseURL, "**"), {
|
|
102
104
|
cache: {
|
|
103
105
|
maxAge: ONE_YEAR_IN_SECONDS
|
|
104
106
|
}
|
|
105
|
-
};
|
|
107
|
+
});
|
|
106
108
|
}
|
|
107
|
-
nuxt.options.nitro.publicAssets ||= [];
|
|
108
109
|
const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
|
|
109
|
-
nuxt.
|
|
110
|
-
|
|
111
|
-
publicAssets
|
|
110
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
111
|
+
nitroConfig.publicAssets ||= [];
|
|
112
|
+
nitroConfig.publicAssets.push({
|
|
112
113
|
dir: cacheDir,
|
|
113
114
|
maxAge: ONE_YEAR_IN_SECONDS,
|
|
114
115
|
baseURL: assetsBaseURL
|
|
115
|
-
}
|
|
116
|
-
prerender
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
});
|
|
117
|
+
nitroConfig.prerender ||= {};
|
|
118
|
+
nitroConfig.prerender.ignore ||= [];
|
|
119
|
+
nitroConfig.prerender.ignore.push(assetsBaseURL);
|
|
119
120
|
});
|
|
120
121
|
return {
|
|
121
122
|
renderedScript
|
|
122
123
|
};
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
function buildProxyConfig(collectPrefix) {
|
|
127
|
+
return {
|
|
128
|
+
googleAnalytics: {
|
|
129
|
+
// GA4: screen/timezone/UA needed for device, time, and OS reports; rest anonymized safely
|
|
130
|
+
privacy: { ip: true, userAgent: false, language: true, screen: false, timezone: false, hardware: true },
|
|
131
|
+
rewrite: [
|
|
132
|
+
// Modern gtag.js uses www.google.com/g/collect
|
|
133
|
+
{ from: "www.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
134
|
+
// Older gtag.js constructs URLs dynamically: "https://"+(subdomain)+".google-analytics.com/g/collect"
|
|
135
|
+
// We need to catch the suffix pattern with leading dot
|
|
136
|
+
{ from: ".google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
137
|
+
{ from: ".analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
138
|
+
// Full domain patterns for static URLs
|
|
139
|
+
{ from: "www.google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
140
|
+
{ from: "analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
141
|
+
// Legacy endpoints still used by some scripts
|
|
142
|
+
{ from: "www.google-analytics.com", to: `${collectPrefix}/ga` },
|
|
143
|
+
{ from: "analytics.google.com", to: `${collectPrefix}/ga` },
|
|
144
|
+
// DoubleClick tracking (used by GA4 for ads/conversions)
|
|
145
|
+
{ from: "stats.g.doubleclick.net/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
146
|
+
{ from: "stats.g.doubleclick.net", to: `${collectPrefix}/ga-dc` },
|
|
147
|
+
// Google Ads/Syndication tracking
|
|
148
|
+
{ from: "pagead2.googlesyndication.com", to: `${collectPrefix}/ga-syn` },
|
|
149
|
+
{ from: "www.googleadservices.com", to: `${collectPrefix}/ga-ads` },
|
|
150
|
+
{ from: "googleads.g.doubleclick.net", to: `${collectPrefix}/ga-gads` }
|
|
151
|
+
],
|
|
152
|
+
routes: {
|
|
153
|
+
[`${collectPrefix}/ga/**`]: { proxy: "https://www.google-analytics.com/**" },
|
|
154
|
+
[`${collectPrefix}/ga-dc/**`]: { proxy: "https://stats.g.doubleclick.net/**" },
|
|
155
|
+
[`${collectPrefix}/ga-syn/**`]: { proxy: "https://pagead2.googlesyndication.com/**" },
|
|
156
|
+
[`${collectPrefix}/ga-ads/**`]: { proxy: "https://www.googleadservices.com/**" },
|
|
157
|
+
[`${collectPrefix}/ga-gads/**`]: { proxy: "https://googleads.g.doubleclick.net/**" }
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
googleTagManager: {
|
|
161
|
+
// GTM: container only, passes data through — downstream tags have their own privacy
|
|
162
|
+
privacy: { ip: false, userAgent: false, language: false, screen: false, timezone: false, hardware: false },
|
|
163
|
+
rewrite: [
|
|
164
|
+
{ from: "www.googletagmanager.com", to: `${collectPrefix}/gtm` }
|
|
165
|
+
],
|
|
166
|
+
routes: {
|
|
167
|
+
[`${collectPrefix}/gtm/**`]: { proxy: "https://www.googletagmanager.com/**" }
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
metaPixel: {
|
|
171
|
+
// Meta: untrusted ad network — full anonymization
|
|
172
|
+
privacy: { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true },
|
|
173
|
+
rewrite: [
|
|
174
|
+
// SDK script loading
|
|
175
|
+
{ from: "connect.facebook.net", to: `${collectPrefix}/meta` },
|
|
176
|
+
// Tracking pixel endpoint (www and non-www)
|
|
177
|
+
{ from: "www.facebook.com/tr", to: `${collectPrefix}/meta-tr` },
|
|
178
|
+
{ from: "facebook.com/tr", to: `${collectPrefix}/meta-tr` },
|
|
179
|
+
// Additional Meta tracking domains
|
|
180
|
+
{ from: "pixel.facebook.com", to: `${collectPrefix}/meta-px` },
|
|
181
|
+
{ from: "www.facebook.com/plugins", to: `${collectPrefix}/meta-plugins` }
|
|
182
|
+
],
|
|
183
|
+
routes: {
|
|
184
|
+
[`${collectPrefix}/meta/**`]: { proxy: "https://connect.facebook.net/**" },
|
|
185
|
+
[`${collectPrefix}/meta-tr/**`]: { proxy: "https://www.facebook.com/tr/**" },
|
|
186
|
+
[`${collectPrefix}/meta-px/**`]: { proxy: "https://pixel.facebook.com/**" },
|
|
187
|
+
[`${collectPrefix}/meta-plugins/**`]: { proxy: "https://www.facebook.com/plugins/**" }
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
tiktokPixel: {
|
|
191
|
+
// TikTok: untrusted ad network — full anonymization
|
|
192
|
+
privacy: { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true },
|
|
193
|
+
rewrite: [
|
|
194
|
+
{ from: "analytics.tiktok.com", to: `${collectPrefix}/tiktok` }
|
|
195
|
+
],
|
|
196
|
+
routes: {
|
|
197
|
+
[`${collectPrefix}/tiktok/**`]: { proxy: "https://analytics.tiktok.com/**" }
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
segment: {
|
|
201
|
+
// Segment: trusted data pipeline — needs maximum fidelity for downstream destinations
|
|
202
|
+
privacy: { ip: false, userAgent: false, language: false, screen: false, timezone: false, hardware: false },
|
|
203
|
+
rewrite: [
|
|
204
|
+
{ from: "api.segment.io", to: `${collectPrefix}/segment` },
|
|
205
|
+
{ from: "cdn.segment.com", to: `${collectPrefix}/segment-cdn` }
|
|
206
|
+
],
|
|
207
|
+
routes: {
|
|
208
|
+
[`${collectPrefix}/segment/**`]: { proxy: "https://api.segment.io/**" },
|
|
209
|
+
[`${collectPrefix}/segment-cdn/**`]: { proxy: "https://cdn.segment.com/**" }
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
xPixel: {
|
|
213
|
+
// X/Twitter: untrusted ad network — full anonymization
|
|
214
|
+
privacy: { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true },
|
|
215
|
+
rewrite: [
|
|
216
|
+
{ from: "analytics.twitter.com", to: `${collectPrefix}/x` },
|
|
217
|
+
{ from: "t.co", to: `${collectPrefix}/x-t` }
|
|
218
|
+
],
|
|
219
|
+
routes: {
|
|
220
|
+
[`${collectPrefix}/x/**`]: { proxy: "https://analytics.twitter.com/**" },
|
|
221
|
+
[`${collectPrefix}/x-t/**`]: { proxy: "https://t.co/**" }
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
snapchatPixel: {
|
|
225
|
+
// Snapchat: untrusted ad network — full anonymization
|
|
226
|
+
privacy: { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true },
|
|
227
|
+
rewrite: [
|
|
228
|
+
{ from: "tr.snapchat.com", to: `${collectPrefix}/snap` }
|
|
229
|
+
],
|
|
230
|
+
routes: {
|
|
231
|
+
[`${collectPrefix}/snap/**`]: { proxy: "https://tr.snapchat.com/**" }
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
redditPixel: {
|
|
235
|
+
// Reddit: untrusted ad network — full anonymization
|
|
236
|
+
privacy: { ip: true, userAgent: true, language: true, screen: true, timezone: true, hardware: true },
|
|
237
|
+
rewrite: [
|
|
238
|
+
{ from: "alb.reddit.com", to: `${collectPrefix}/reddit` }
|
|
239
|
+
],
|
|
240
|
+
routes: {
|
|
241
|
+
[`${collectPrefix}/reddit/**`]: { proxy: "https://alb.reddit.com/**" }
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
clarity: {
|
|
245
|
+
// Clarity: screen/UA/timezone needed for heatmaps and device filtering; rest anonymized
|
|
246
|
+
privacy: { ip: true, userAgent: false, language: true, screen: false, timezone: false, hardware: true },
|
|
247
|
+
rewrite: [
|
|
248
|
+
// Main clarity domain
|
|
249
|
+
{ from: "www.clarity.ms", to: `${collectPrefix}/clarity` },
|
|
250
|
+
// Script loader (the actual SDK is loaded from here)
|
|
251
|
+
{ from: "scripts.clarity.ms", to: `${collectPrefix}/clarity-scripts` },
|
|
252
|
+
// Data collection endpoint
|
|
253
|
+
{ from: "d.clarity.ms", to: `${collectPrefix}/clarity-data` },
|
|
254
|
+
// Event collection endpoint
|
|
255
|
+
{ from: "e.clarity.ms", to: `${collectPrefix}/clarity-events` }
|
|
256
|
+
],
|
|
257
|
+
routes: {
|
|
258
|
+
[`${collectPrefix}/clarity/**`]: { proxy: "https://www.clarity.ms/**" },
|
|
259
|
+
[`${collectPrefix}/clarity-scripts/**`]: { proxy: "https://scripts.clarity.ms/**" },
|
|
260
|
+
[`${collectPrefix}/clarity-data/**`]: { proxy: "https://d.clarity.ms/**" },
|
|
261
|
+
[`${collectPrefix}/clarity-events/**`]: { proxy: "https://e.clarity.ms/**" }
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
posthog: {
|
|
265
|
+
// No rewrites needed - PostHog uses NPM mode, SDK URLs are set via api_host config
|
|
266
|
+
// PostHog: needs real IP for GeoIP enrichment + feature flag targeting
|
|
267
|
+
privacy: { ip: false, userAgent: false, language: false, screen: false, timezone: false, hardware: false },
|
|
268
|
+
routes: {
|
|
269
|
+
// US region
|
|
270
|
+
[`${collectPrefix}/ph/static/**`]: { proxy: "https://us-assets.i.posthog.com/static/**" },
|
|
271
|
+
[`${collectPrefix}/ph/**`]: { proxy: "https://us.i.posthog.com/**" },
|
|
272
|
+
// EU region
|
|
273
|
+
[`${collectPrefix}/ph-eu/static/**`]: { proxy: "https://eu-assets.i.posthog.com/static/**" },
|
|
274
|
+
[`${collectPrefix}/ph-eu/**`]: { proxy: "https://eu.i.posthog.com/**" }
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
hotjar: {
|
|
278
|
+
// Hotjar: screen/UA/timezone needed for heatmaps and device segmentation; rest anonymized
|
|
279
|
+
privacy: { ip: true, userAgent: false, language: true, screen: false, timezone: false, hardware: true },
|
|
280
|
+
rewrite: [
|
|
281
|
+
// Static assets
|
|
282
|
+
{ from: "static.hotjar.com", to: `${collectPrefix}/hotjar` },
|
|
283
|
+
// Script loader (bootstrap script loads the main SDK from here)
|
|
284
|
+
{ from: "script.hotjar.com", to: `${collectPrefix}/hotjar-script` },
|
|
285
|
+
// Configuration/variables
|
|
286
|
+
{ from: "vars.hotjar.com", to: `${collectPrefix}/hotjar-vars` },
|
|
287
|
+
// Data ingestion endpoint
|
|
288
|
+
{ from: "in.hotjar.com", to: `${collectPrefix}/hotjar-in` },
|
|
289
|
+
// Video capture
|
|
290
|
+
{ from: "vc.hotjar.com", to: `${collectPrefix}/hotjar-vc` },
|
|
291
|
+
// Metrics/telemetry
|
|
292
|
+
{ from: "metrics.hotjar.io", to: `${collectPrefix}/hotjar-metrics` },
|
|
293
|
+
// Insights (ContentSquare integration)
|
|
294
|
+
{ from: "insights.hotjar.com", to: `${collectPrefix}/hotjar-insights` }
|
|
295
|
+
],
|
|
296
|
+
routes: {
|
|
297
|
+
[`${collectPrefix}/hotjar/**`]: { proxy: "https://static.hotjar.com/**" },
|
|
298
|
+
[`${collectPrefix}/hotjar-script/**`]: { proxy: "https://script.hotjar.com/**" },
|
|
299
|
+
[`${collectPrefix}/hotjar-vars/**`]: { proxy: "https://vars.hotjar.com/**" },
|
|
300
|
+
[`${collectPrefix}/hotjar-in/**`]: { proxy: "https://in.hotjar.com/**" },
|
|
301
|
+
[`${collectPrefix}/hotjar-vc/**`]: { proxy: "https://vc.hotjar.com/**" },
|
|
302
|
+
[`${collectPrefix}/hotjar-metrics/**`]: { proxy: "https://metrics.hotjar.io/**" },
|
|
303
|
+
[`${collectPrefix}/hotjar-insights/**`]: { proxy: "https://insights.hotjar.com/**" }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function getProxyConfig(key, collectPrefix) {
|
|
309
|
+
const configs = buildProxyConfig(collectPrefix);
|
|
310
|
+
return configs[key];
|
|
311
|
+
}
|
|
312
|
+
function getAllProxyConfigs(collectPrefix) {
|
|
313
|
+
return buildProxyConfig(collectPrefix);
|
|
314
|
+
}
|
|
315
|
+
function getSWInterceptRules(collectPrefix) {
|
|
316
|
+
const configs = buildProxyConfig(collectPrefix);
|
|
317
|
+
const rules = [];
|
|
318
|
+
for (const config of Object.values(configs)) {
|
|
319
|
+
if (!config.routes)
|
|
320
|
+
continue;
|
|
321
|
+
for (const [localPath, { proxy }] of Object.entries(config.routes)) {
|
|
322
|
+
const match = proxy.match(/^https?:\/\/([^/]+)(\/.*)?\/\*\*$/);
|
|
323
|
+
if (match?.[1]) {
|
|
324
|
+
const domain = match[1];
|
|
325
|
+
const pathPrefix = match[2] || "";
|
|
326
|
+
const target = localPath.replace(/\/\*\*$/, "");
|
|
327
|
+
rules.push({ pattern: domain, pathPrefix, target });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return rules;
|
|
332
|
+
}
|
|
333
|
+
|
|
125
334
|
function isVue(id, opts = {}) {
|
|
126
335
|
const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
|
|
127
336
|
if (id.endsWith(".vue") && !search) {
|
|
@@ -178,7 +387,7 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
|
|
|
178
387
|
return { url: src };
|
|
179
388
|
}
|
|
180
389
|
async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
181
|
-
const { src, url, filename, forceDownload, integrity } = opts;
|
|
390
|
+
const { src, url, filename, forceDownload, integrity, proxyRewrites } = opts;
|
|
182
391
|
if (src === url || !filename) {
|
|
183
392
|
return;
|
|
184
393
|
}
|
|
@@ -186,7 +395,8 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
|
186
395
|
const scriptContent = renderedScript.get(src);
|
|
187
396
|
let res = scriptContent instanceof Error ? void 0 : scriptContent?.content;
|
|
188
397
|
if (!res) {
|
|
189
|
-
const
|
|
398
|
+
const proxyRewritesHash = proxyRewrites?.length ? `-${hash(proxyRewrites)}` : "";
|
|
399
|
+
const cacheKey = proxyRewrites?.length ? `bundle-proxy:${filename.replace(".js", `${proxyRewritesHash}.js`)}` : `bundle:${filename}`;
|
|
190
400
|
const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !await isCacheExpired(storage, filename, cacheMaxAge);
|
|
191
401
|
if (shouldUseCache) {
|
|
192
402
|
const cachedContent = await storage.getItemRaw(cacheKey);
|
|
@@ -212,8 +422,15 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
|
212
422
|
size = contentLength ? Number(contentLength) / 1024 : 0;
|
|
213
423
|
return Buffer.from(r._data || await r.arrayBuffer());
|
|
214
424
|
});
|
|
215
|
-
const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
|
|
216
425
|
await storage.setItemRaw(`bundle:${filename}`, res);
|
|
426
|
+
if (proxyRewrites?.length && res) {
|
|
427
|
+
const content = res.toString("utf-8");
|
|
428
|
+
const rewritten = rewriteScriptUrls(content, proxyRewrites);
|
|
429
|
+
res = Buffer.from(rewritten, "utf-8");
|
|
430
|
+
logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
|
|
431
|
+
}
|
|
432
|
+
const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
|
|
433
|
+
await storage.setItemRaw(cacheKey, res);
|
|
217
434
|
await storage.setItem(`bundle-meta:${filename}`, {
|
|
218
435
|
timestamp: Date.now(),
|
|
219
436
|
src,
|
|
@@ -262,16 +479,21 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
262
479
|
return createUnplugin(() => {
|
|
263
480
|
return {
|
|
264
481
|
name: "nuxt:scripts:bundler-transformer",
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
482
|
+
transform: {
|
|
483
|
+
filter: {
|
|
484
|
+
id: {
|
|
485
|
+
include: [/\.vue/, /\.[cm]?[jt]sx?$/],
|
|
486
|
+
exclude: [/\.(?:test|spec)\./]
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
async handler(code, id) {
|
|
490
|
+
if (!isVue(id, { type: ["template", "script"] }) && !isJS(id))
|
|
491
|
+
return;
|
|
492
|
+
if (!code.includes("useScript"))
|
|
493
|
+
return;
|
|
494
|
+
const s = new MagicString(code);
|
|
495
|
+
const deferredOps = [];
|
|
496
|
+
parseAndWalk(code, id, function(_node) {
|
|
275
497
|
const calleeName = _node.callee?.name;
|
|
276
498
|
if (!calleeName)
|
|
277
499
|
return;
|
|
@@ -281,6 +503,11 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
281
503
|
const node = _node;
|
|
282
504
|
let scriptSrcNode;
|
|
283
505
|
let src;
|
|
506
|
+
let registryKey;
|
|
507
|
+
if (fnName !== "useScript") {
|
|
508
|
+
const baseName = fnName.replace(/^useScript/, "");
|
|
509
|
+
registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : void 0;
|
|
510
|
+
}
|
|
284
511
|
if (fnName === "useScript") {
|
|
285
512
|
if (node.arguments[0]?.type === "Literal") {
|
|
286
513
|
scriptSrcNode = node.arguments[0];
|
|
@@ -297,9 +524,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
297
524
|
}
|
|
298
525
|
if (!registryNode.scriptBundling && !registryNode.src)
|
|
299
526
|
return;
|
|
300
|
-
const
|
|
301
|
-
const registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : "";
|
|
302
|
-
const registryConfig = options.registryConfig?.[registryKey] || {};
|
|
527
|
+
const registryConfig = options.registryConfig?.[registryKey || ""] || {};
|
|
303
528
|
const fnArg0 = {};
|
|
304
529
|
if (node.arguments[0]?.type === "ObjectExpression") {
|
|
305
530
|
const optionsNode = node.arguments[0];
|
|
@@ -319,6 +544,8 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
319
544
|
src = registryNode.scriptBundling && registryNode.scriptBundling(mergedOptions);
|
|
320
545
|
if (src === false)
|
|
321
546
|
return;
|
|
547
|
+
if (!src && registryNode.src)
|
|
548
|
+
src = registryNode.src;
|
|
322
549
|
}
|
|
323
550
|
}
|
|
324
551
|
if (!scriptSrcNode && !src) {
|
|
@@ -333,8 +560,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
333
560
|
if (bundleProperty && bundleProperty.value.type === "Literal") {
|
|
334
561
|
const bundleValue = bundleProperty.value.value;
|
|
335
562
|
if (bundleValue === true || bundleValue === "force" || String(bundleValue) === "true") {
|
|
336
|
-
|
|
337
|
-
s.overwrite(valueNode.start, valueNode.end, `'unsupported'`);
|
|
563
|
+
s.overwrite(bundleProperty.value.start, bundleProperty.value.end, `'unsupported'`);
|
|
338
564
|
}
|
|
339
565
|
}
|
|
340
566
|
}
|
|
@@ -351,8 +577,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
351
577
|
(p) => (p.key?.name === "bundle" || p.key?.value === "bundle") && p.type === "Property"
|
|
352
578
|
);
|
|
353
579
|
if (bundleProperty && bundleProperty.value.type === "Literal") {
|
|
354
|
-
const
|
|
355
|
-
const bundleValue = value.value;
|
|
580
|
+
const bundleValue = bundleProperty.value.value;
|
|
356
581
|
if (bundleValue !== true && bundleValue !== "force" && String(bundleValue) !== "true") {
|
|
357
582
|
canBundle = false;
|
|
358
583
|
return;
|
|
@@ -380,81 +605,104 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
380
605
|
canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
|
|
381
606
|
forceDownload = bundleValue === "force";
|
|
382
607
|
}
|
|
608
|
+
const firstPartyOption = scriptOptions?.value.properties?.find((prop) => {
|
|
609
|
+
return prop.type === "Property" && prop.key?.name === "firstParty" && prop.value.type === "Literal";
|
|
610
|
+
});
|
|
611
|
+
let firstPartyOptOut = firstPartyOption?.value.value === false;
|
|
612
|
+
if (!firstPartyOptOut && node.arguments[1]?.type === "ObjectExpression") {
|
|
613
|
+
const secondArgFirstPartyProp = node.arguments[1].properties.find(
|
|
614
|
+
(p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
|
|
615
|
+
);
|
|
616
|
+
firstPartyOptOut = secondArgFirstPartyProp?.value.value === false;
|
|
617
|
+
}
|
|
618
|
+
if (!firstPartyOptOut && node.arguments[0]?.type === "ObjectExpression") {
|
|
619
|
+
const firstArgFirstPartyProp = node.arguments[0].properties.find(
|
|
620
|
+
(p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
|
|
621
|
+
);
|
|
622
|
+
firstPartyOptOut = firstArgFirstPartyProp?.value.value === false;
|
|
623
|
+
}
|
|
383
624
|
if (canBundle) {
|
|
384
625
|
const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
url
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
626
|
+
const script = options.scripts?.find((s2) => s2.import.name === fnName);
|
|
627
|
+
const proxyConfigKey = script?.proxy !== false ? script?.proxy || registryKey : void 0;
|
|
628
|
+
const proxyRewrites = options.firstPartyEnabled && !firstPartyOptOut && proxyConfigKey && options.firstPartyCollectPrefix ? getProxyConfig(proxyConfigKey, options.firstPartyCollectPrefix)?.rewrite : void 0;
|
|
629
|
+
deferredOps.push(async () => {
|
|
630
|
+
let url = _url;
|
|
631
|
+
try {
|
|
632
|
+
await downloadScript({ src, url, filename, forceDownload, proxyRewrites, integrity: options.integrity }, renderedScript, options.fetchOptions, options.cacheMaxAge);
|
|
633
|
+
} catch (e) {
|
|
634
|
+
if (options.fallbackOnSrcOnBundleFail) {
|
|
635
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
|
|
636
|
+
url = src;
|
|
637
|
+
} else {
|
|
638
|
+
const errorMessage = e?.message || "Unknown error";
|
|
639
|
+
if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("certificate")) {
|
|
640
|
+
logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
|
|
641
|
+
logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
|
|
642
|
+
}
|
|
643
|
+
throw e;
|
|
397
644
|
}
|
|
398
|
-
throw e;
|
|
399
645
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
|
|
406
|
-
}
|
|
407
|
-
const scriptMeta = renderedScript.get(url);
|
|
408
|
-
const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
|
|
409
|
-
if (scriptSrcNode) {
|
|
410
|
-
if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
|
|
411
|
-
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
|
|
412
|
-
} else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
|
|
413
|
-
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
414
|
-
const objArg = node.arguments[0];
|
|
415
|
-
s.appendLeft(objArg.end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
|
|
416
|
-
} else {
|
|
417
|
-
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
646
|
+
if (src === url) {
|
|
647
|
+
if (src && src.startsWith("/"))
|
|
648
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
|
|
649
|
+
else
|
|
650
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
|
|
418
651
|
}
|
|
419
|
-
|
|
420
|
-
const
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
652
|
+
const scriptMeta = renderedScript.get(url);
|
|
653
|
+
const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
|
|
654
|
+
if (scriptSrcNode) {
|
|
655
|
+
if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
|
|
656
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
|
|
657
|
+
} else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
|
|
658
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
659
|
+
s.appendLeft(node.arguments[0].end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
|
|
660
|
+
} else {
|
|
661
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
662
|
+
}
|
|
663
|
+
} else {
|
|
664
|
+
const integrityProps = integrityHash ? `, integrity: '${integrityHash}', crossorigin: 'anonymous'` : "";
|
|
665
|
+
if (node.arguments[0]) {
|
|
666
|
+
const optionsNode = node.arguments[0];
|
|
667
|
+
const scriptInputProperty = optionsNode.properties.find(
|
|
668
|
+
(p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
|
|
669
|
+
);
|
|
670
|
+
if (scriptInputProperty) {
|
|
671
|
+
const scriptInput = scriptInputProperty.value;
|
|
672
|
+
if (scriptInput.type === "ObjectExpression") {
|
|
673
|
+
const srcProperty = scriptInput.properties.find(
|
|
674
|
+
(p) => p.key?.name === "src" || p.key?.value === "src"
|
|
675
|
+
);
|
|
676
|
+
if (srcProperty) {
|
|
677
|
+
s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
|
|
678
|
+
if (integrityHash)
|
|
679
|
+
s.appendLeft(scriptInput.end - 1, integrityProps);
|
|
680
|
+
} else {
|
|
681
|
+
s.appendRight(scriptInput.end - 1, `, src: '${url}'${integrityProps}`);
|
|
682
|
+
}
|
|
438
683
|
}
|
|
684
|
+
} else {
|
|
685
|
+
s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
|
|
439
686
|
}
|
|
440
687
|
} else {
|
|
441
|
-
s.appendRight(node.
|
|
688
|
+
s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
|
|
442
689
|
}
|
|
443
|
-
} else {
|
|
444
|
-
s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
|
|
445
690
|
}
|
|
446
|
-
}
|
|
691
|
+
});
|
|
447
692
|
}
|
|
448
693
|
}
|
|
449
694
|
}
|
|
450
695
|
}
|
|
696
|
+
});
|
|
697
|
+
for (const op of deferredOps) {
|
|
698
|
+
await op();
|
|
699
|
+
}
|
|
700
|
+
if (s.hasChanged()) {
|
|
701
|
+
return {
|
|
702
|
+
code: s.toString(),
|
|
703
|
+
map: s.generateMap({ includeContent: true, source: id })
|
|
704
|
+
};
|
|
451
705
|
}
|
|
452
|
-
});
|
|
453
|
-
if (s.hasChanged()) {
|
|
454
|
-
return {
|
|
455
|
-
code: s.toString(),
|
|
456
|
-
map: s.generateMap({ includeContent: true, source: id })
|
|
457
|
-
};
|
|
458
706
|
}
|
|
459
707
|
}
|
|
460
708
|
};
|
|
@@ -505,17 +753,18 @@ function NuxtScriptsCheckScripts() {
|
|
|
505
753
|
return createUnplugin(() => {
|
|
506
754
|
return {
|
|
507
755
|
name: "nuxt-scripts:check-scripts",
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
756
|
+
transform: {
|
|
757
|
+
filter: {
|
|
758
|
+
id: /\.vue/
|
|
759
|
+
},
|
|
760
|
+
handler(code, id) {
|
|
761
|
+
if (!isVue(id, { type: ["script"] }))
|
|
762
|
+
return;
|
|
763
|
+
if (!code.includes("useScript"))
|
|
764
|
+
return;
|
|
765
|
+
let nameNode;
|
|
766
|
+
let errorNode;
|
|
767
|
+
parseAndWalk(code, id, function(_node) {
|
|
519
768
|
if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
|
|
520
769
|
const objPattern = _node.declarations[0]?.id;
|
|
521
770
|
for (const property of objPattern.properties) {
|
|
@@ -545,10 +794,10 @@ function NuxtScriptsCheckScripts() {
|
|
|
545
794
|
}
|
|
546
795
|
}
|
|
547
796
|
}
|
|
797
|
+
});
|
|
798
|
+
if (errorNode) {
|
|
799
|
+
return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
|
|
548
800
|
}
|
|
549
|
-
});
|
|
550
|
-
if (errorNode) {
|
|
551
|
-
return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
|
|
552
801
|
}
|
|
553
802
|
}
|
|
554
803
|
};
|
|
@@ -596,6 +845,7 @@ export {}`;
|
|
|
596
845
|
function templateTriggerResolver(defaultScriptOptions) {
|
|
597
846
|
const needsIdleTimeout = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "idleTimeout" in defaultScriptOptions.trigger;
|
|
598
847
|
const needsInteraction = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "interaction" in defaultScriptOptions.trigger;
|
|
848
|
+
const needsServiceWorker = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "serviceWorker" in defaultScriptOptions.trigger;
|
|
599
849
|
const imports = [];
|
|
600
850
|
if (needsIdleTimeout) {
|
|
601
851
|
imports.push(`import { useScriptTriggerIdleTimeout } from '#nuxt-scripts/composables/useScriptTriggerIdleTimeout'`);
|
|
@@ -603,11 +853,15 @@ function templateTriggerResolver(defaultScriptOptions) {
|
|
|
603
853
|
if (needsInteraction) {
|
|
604
854
|
imports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
|
|
605
855
|
}
|
|
856
|
+
if (needsServiceWorker) {
|
|
857
|
+
imports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
|
|
858
|
+
}
|
|
606
859
|
return [
|
|
607
860
|
...imports,
|
|
608
861
|
`export function resolveTrigger(trigger) {`,
|
|
609
862
|
needsIdleTimeout ? ` if ('idleTimeout' in trigger) return useScriptTriggerIdleTimeout({ timeout: trigger.idleTimeout })` : "",
|
|
610
863
|
needsInteraction ? ` if ('interaction' in trigger) return useScriptTriggerInteraction({ events: trigger.interaction })` : "",
|
|
864
|
+
needsServiceWorker ? ` if ('serviceWorker' in trigger) return useScriptTriggerServiceWorker()` : "",
|
|
611
865
|
` return null`,
|
|
612
866
|
`}`
|
|
613
867
|
].filter(Boolean).join("\n");
|
|
@@ -624,6 +878,9 @@ function resolveTriggerForTemplate(trigger) {
|
|
|
624
878
|
if ("interaction" in trigger) {
|
|
625
879
|
return `useScriptTriggerInteraction({ events: ${JSON.stringify(trigger.interaction)} })`;
|
|
626
880
|
}
|
|
881
|
+
if ("serviceWorker" in trigger) {
|
|
882
|
+
return `useScriptTriggerServiceWorker()`;
|
|
883
|
+
}
|
|
627
884
|
}
|
|
628
885
|
return null;
|
|
629
886
|
}
|
|
@@ -634,27 +891,34 @@ function templatePlugin(config, registry) {
|
|
|
634
891
|
}
|
|
635
892
|
const imports = [];
|
|
636
893
|
const inits = [];
|
|
894
|
+
const resolvedRegistryKeys = [];
|
|
637
895
|
let needsIdleTimeoutImport = false;
|
|
638
896
|
let needsInteractionImport = false;
|
|
897
|
+
let needsServiceWorkerImport = false;
|
|
639
898
|
for (const [k, c] of Object.entries(config.registry || {})) {
|
|
640
|
-
const importDefinition = registry.find((i) => i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
|
|
899
|
+
const importDefinition = registry.find((i) => i.proxy === k || i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
|
|
641
900
|
if (importDefinition) {
|
|
901
|
+
resolvedRegistryKeys.push(k);
|
|
642
902
|
imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
|
|
643
|
-
const args = (typeof c !== "object" ? {} : c) || {};
|
|
644
903
|
if (c === "mock") {
|
|
645
|
-
|
|
646
|
-
} else if (Array.isArray(c) && c.length === 2
|
|
647
|
-
const
|
|
904
|
+
inits.push(`const ${k} = ${importDefinition.import.name}({ scriptOptions: { trigger: 'manual', skipValidation: true } })`);
|
|
905
|
+
} else if (Array.isArray(c) && c.length === 2) {
|
|
906
|
+
const input = c[0] || {};
|
|
907
|
+
const scriptOptions = { ...c[1] };
|
|
908
|
+
const triggerResolved = resolveTriggerForTemplate(scriptOptions?.trigger);
|
|
648
909
|
if (triggerResolved) {
|
|
649
|
-
|
|
650
|
-
if (args.scriptOptions) {
|
|
651
|
-
args.scriptOptions.trigger = `__TRIGGER_${triggerResolved}__`;
|
|
652
|
-
}
|
|
910
|
+
scriptOptions.trigger = "__TRIGGER_PLACEHOLDER__";
|
|
653
911
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
654
912
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
913
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
655
914
|
}
|
|
915
|
+
const args = { ...input, scriptOptions };
|
|
916
|
+
const argsJson = triggerResolved ? JSON.stringify(args).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved) : JSON.stringify(args);
|
|
917
|
+
inits.push(`const ${k} = ${importDefinition.import.name}(${argsJson})`);
|
|
918
|
+
} else {
|
|
919
|
+
const args = (typeof c !== "object" ? {} : c) || {};
|
|
920
|
+
inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args)})`);
|
|
656
921
|
}
|
|
657
|
-
inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args).replace(/"__TRIGGER_(.*?)__"/g, "$1")})`);
|
|
658
922
|
}
|
|
659
923
|
}
|
|
660
924
|
for (const [k, c] of Object.entries(config.globals || {})) {
|
|
@@ -666,8 +930,10 @@ function templatePlugin(config, registry) {
|
|
|
666
930
|
if (triggerResolved) {
|
|
667
931
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
668
932
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
669
|
-
|
|
670
|
-
|
|
933
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
934
|
+
const resolvedOptions = { ...options, trigger: "__TRIGGER_PLACEHOLDER__" };
|
|
935
|
+
const optionsJson = JSON.stringify(resolvedOptions).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
|
|
936
|
+
inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${optionsJson}, use: () => ({ ${k}: window.${k} }) })`);
|
|
671
937
|
} else {
|
|
672
938
|
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} }) })`);
|
|
673
939
|
}
|
|
@@ -676,8 +942,10 @@ function templatePlugin(config, registry) {
|
|
|
676
942
|
if (triggerResolved) {
|
|
677
943
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
678
944
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
679
|
-
|
|
680
|
-
|
|
945
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
946
|
+
const resolvedOptions = { ...c, trigger: "__TRIGGER_PLACEHOLDER__" };
|
|
947
|
+
const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
|
|
948
|
+
inits.push(`const ${k} = useScript(${argsJson}, { use: () => ({ ${k}: window.${k} }) })`);
|
|
681
949
|
} else {
|
|
682
950
|
inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
|
|
683
951
|
}
|
|
@@ -690,6 +958,9 @@ function templatePlugin(config, registry) {
|
|
|
690
958
|
if (needsInteractionImport) {
|
|
691
959
|
triggerImports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
|
|
692
960
|
}
|
|
961
|
+
if (needsServiceWorkerImport) {
|
|
962
|
+
triggerImports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
|
|
963
|
+
}
|
|
693
964
|
return [
|
|
694
965
|
`import { useScript } from '#nuxt-scripts/composables/useScript'`,
|
|
695
966
|
`import { defineNuxtPlugin } from 'nuxt/app'`,
|
|
@@ -702,12 +973,26 @@ function templatePlugin(config, registry) {
|
|
|
702
973
|
` parallel: true,`,
|
|
703
974
|
` setup() {`,
|
|
704
975
|
...inits.map((i) => ` ${i}`),
|
|
705
|
-
` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...
|
|
976
|
+
` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
|
|
706
977
|
` }`,
|
|
707
978
|
`})`
|
|
708
979
|
].join("\n");
|
|
709
980
|
}
|
|
710
981
|
|
|
982
|
+
const PARTYTOWN_FORWARDS = {
|
|
983
|
+
googleAnalytics: ["dataLayer.push", "gtag"],
|
|
984
|
+
plausible: ["plausible"],
|
|
985
|
+
fathom: ["fathom", "fathom.trackEvent", "fathom.trackPageview"],
|
|
986
|
+
umami: ["umami", "umami.track"],
|
|
987
|
+
matomo: ["_paq.push"],
|
|
988
|
+
segment: ["analytics", "analytics.track", "analytics.page", "analytics.identify"],
|
|
989
|
+
metaPixel: ["fbq"],
|
|
990
|
+
xPixel: ["twq"],
|
|
991
|
+
tiktokPixel: ["ttq.track", "ttq.page", "ttq.identify"],
|
|
992
|
+
snapchatPixel: ["snaptr"],
|
|
993
|
+
redditPixel: ["rdt"],
|
|
994
|
+
cloudflareWebAnalytics: ["__cfBeacon"]
|
|
995
|
+
};
|
|
711
996
|
const module$1 = defineNuxtModule({
|
|
712
997
|
meta: {
|
|
713
998
|
name: "@nuxt/scripts",
|
|
@@ -717,6 +1002,7 @@ const module$1 = defineNuxtModule({
|
|
|
717
1002
|
}
|
|
718
1003
|
},
|
|
719
1004
|
defaults: {
|
|
1005
|
+
firstParty: true,
|
|
720
1006
|
defaultScriptOptions: {
|
|
721
1007
|
trigger: "onNuxtReady"
|
|
722
1008
|
},
|
|
@@ -756,7 +1042,8 @@ const module$1 = defineNuxtModule({
|
|
|
756
1042
|
nuxt.options.runtimeConfig["nuxt-scripts"] = {
|
|
757
1043
|
version,
|
|
758
1044
|
// Private proxy config with API key (server-side only)
|
|
759
|
-
googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0
|
|
1045
|
+
googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0,
|
|
1046
|
+
swTemplate: readFileSync(await resolvePath("./runtime/sw/proxy-sw.template.js"), "utf-8")
|
|
760
1047
|
};
|
|
761
1048
|
nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
|
|
762
1049
|
// expose for devtools
|
|
@@ -772,13 +1059,57 @@ const module$1 = defineNuxtModule({
|
|
|
772
1059
|
config.registry
|
|
773
1060
|
);
|
|
774
1061
|
}
|
|
1062
|
+
if (config.defaultScriptOptions?.bundle !== void 0) {
|
|
1063
|
+
logger.warn(
|
|
1064
|
+
"`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
|
|
1068
|
+
const preset = process.env.NITRO_PRESET || "";
|
|
1069
|
+
const isStaticPreset = staticPresets.includes(preset);
|
|
1070
|
+
const firstPartyEnabled = !!config.firstParty;
|
|
1071
|
+
const firstPartyPrefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
|
|
1072
|
+
const firstPartyCollectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
|
|
1073
|
+
const firstPartyPrivacy = typeof config.firstParty === "object" ? config.firstParty.privacy : void 0;
|
|
1074
|
+
const assetsPrefix = firstPartyPrefix || config.assets?.prefix || "/_scripts";
|
|
1075
|
+
if (config.partytown?.length) {
|
|
1076
|
+
config.registry = config.registry || {};
|
|
1077
|
+
const requiredForwards = [];
|
|
1078
|
+
for (const scriptKey of config.partytown) {
|
|
1079
|
+
const forwards = PARTYTOWN_FORWARDS[scriptKey];
|
|
1080
|
+
if (forwards) {
|
|
1081
|
+
requiredForwards.push(...forwards);
|
|
1082
|
+
} else if (import.meta.dev) {
|
|
1083
|
+
logger.warn(`[partytown] "${scriptKey}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
|
|
1084
|
+
}
|
|
1085
|
+
const reg = config.registry;
|
|
1086
|
+
const existing = reg[scriptKey];
|
|
1087
|
+
if (Array.isArray(existing)) {
|
|
1088
|
+
existing[1] = { ...existing[1], partytown: true };
|
|
1089
|
+
} else if (existing && typeof existing === "object" && existing !== true && existing !== "mock") {
|
|
1090
|
+
reg[scriptKey] = [existing, { partytown: true }];
|
|
1091
|
+
} else if (existing === true || existing === "mock") {
|
|
1092
|
+
reg[scriptKey] = [{}, { partytown: true }];
|
|
1093
|
+
} else {
|
|
1094
|
+
reg[scriptKey] = [{}, { partytown: true }];
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (requiredForwards.length && hasNuxtModule("@nuxtjs/partytown")) {
|
|
1098
|
+
const partytownConfig = nuxt.options.partytown || {};
|
|
1099
|
+
const existingForwards = partytownConfig.forward || [];
|
|
1100
|
+
const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...requiredForwards])];
|
|
1101
|
+
nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
|
|
1102
|
+
logger.info(`[partytown] Auto-configured forwards: ${requiredForwards.join(", ")}`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
775
1105
|
const composables = [
|
|
776
1106
|
"useScript",
|
|
777
1107
|
"useScriptEventPage",
|
|
778
1108
|
"useScriptTriggerConsent",
|
|
779
1109
|
"useScriptTriggerElement",
|
|
780
1110
|
"useScriptTriggerIdleTimeout",
|
|
781
|
-
"useScriptTriggerInteraction"
|
|
1111
|
+
"useScriptTriggerInteraction",
|
|
1112
|
+
"useScriptTriggerServiceWorker"
|
|
782
1113
|
];
|
|
783
1114
|
for (const composable of composables) {
|
|
784
1115
|
addImports({
|
|
@@ -798,6 +1129,104 @@ const module$1 = defineNuxtModule({
|
|
|
798
1129
|
return templateTriggerResolver(config.defaultScriptOptions);
|
|
799
1130
|
}
|
|
800
1131
|
});
|
|
1132
|
+
const swHandlerPath = await resolvePath("./runtime/server/sw-handler");
|
|
1133
|
+
logger.debug("[nuxt-scripts] First-party config:", { firstPartyEnabled, firstPartyPrivacy, firstPartyCollectPrefix });
|
|
1134
|
+
if (firstPartyEnabled && !nuxt.options.dev) {
|
|
1135
|
+
const swPath = "/_nuxt-scripts-sw.js";
|
|
1136
|
+
const swRules = getSWInterceptRules(firstPartyCollectPrefix);
|
|
1137
|
+
addServerHandler({
|
|
1138
|
+
route: swPath,
|
|
1139
|
+
handler: swHandlerPath
|
|
1140
|
+
});
|
|
1141
|
+
addPluginTemplate({
|
|
1142
|
+
filename: "nuxt-scripts-sw-register.client.mjs",
|
|
1143
|
+
getContents() {
|
|
1144
|
+
return `import { defineNuxtPlugin } from 'nuxt/app'
|
|
1145
|
+
|
|
1146
|
+
export default defineNuxtPlugin({
|
|
1147
|
+
name: 'nuxt-scripts:sw-register',
|
|
1148
|
+
enforce: 'pre',
|
|
1149
|
+
async setup() {
|
|
1150
|
+
if (!('serviceWorker' in navigator)) return;
|
|
1151
|
+
|
|
1152
|
+
try {
|
|
1153
|
+
const reg = await navigator.serviceWorker.register('${swPath}', { scope: '/' });
|
|
1154
|
+
|
|
1155
|
+
// Wait for SW to be active and controlling this page
|
|
1156
|
+
if (!navigator.serviceWorker.controller) {
|
|
1157
|
+
await new Promise((resolve) => {
|
|
1158
|
+
const onControllerChange = () => {
|
|
1159
|
+
navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
|
|
1160
|
+
resolve();
|
|
1161
|
+
};
|
|
1162
|
+
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
1163
|
+
|
|
1164
|
+
// Fallback timeout
|
|
1165
|
+
setTimeout(resolve, 2000);
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
} catch (err) {
|
|
1169
|
+
console.warn('[nuxt-scripts] SW registration failed:', err);
|
|
1170
|
+
}
|
|
1171
|
+
},
|
|
1172
|
+
})
|
|
1173
|
+
`;
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
addPluginTemplate({
|
|
1177
|
+
filename: "nuxt-scripts-beacon-intercept.client.mjs",
|
|
1178
|
+
getContents() {
|
|
1179
|
+
const rulesJson = JSON.stringify(swRules);
|
|
1180
|
+
return `export default defineNuxtPlugin({
|
|
1181
|
+
name: 'nuxt-scripts:beacon-intercept',
|
|
1182
|
+
enforce: 'pre',
|
|
1183
|
+
setup() {
|
|
1184
|
+
if (typeof navigator === 'undefined' || !navigator.sendBeacon) return;
|
|
1185
|
+
|
|
1186
|
+
const rules = ${rulesJson};
|
|
1187
|
+
const originalBeacon = navigator.sendBeacon.bind(navigator);
|
|
1188
|
+
|
|
1189
|
+
navigator.sendBeacon = (url, data) => {
|
|
1190
|
+
try {
|
|
1191
|
+
const parsed = new URL(url, window.location.origin);
|
|
1192
|
+
|
|
1193
|
+
// Check if this URL matches any of our proxy rules
|
|
1194
|
+
for (const rule of rules) {
|
|
1195
|
+
if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
|
|
1196
|
+
// Check path prefix if specified
|
|
1197
|
+
if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) {
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// Rewrite to proxy: strip pathPrefix from original, prepend target
|
|
1202
|
+
const pathWithoutPrefix = rule.pathPrefix
|
|
1203
|
+
? parsed.pathname.slice(rule.pathPrefix.length)
|
|
1204
|
+
: parsed.pathname;
|
|
1205
|
+
const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
|
|
1206
|
+
const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;
|
|
1207
|
+
|
|
1208
|
+
return originalBeacon(proxyUrl, data);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
} catch (e) {
|
|
1212
|
+
// URL parsing failed, pass through
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
return originalBeacon(url, data);
|
|
1216
|
+
};
|
|
1217
|
+
},
|
|
1218
|
+
})
|
|
1219
|
+
`;
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
nuxt.options.runtimeConfig.public["nuxt-scripts-sw"] = { path: swPath };
|
|
1223
|
+
const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
|
|
1224
|
+
logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstPartyCollectPrefix}/**`, "->", proxyHandlerPath);
|
|
1225
|
+
addServerHandler({
|
|
1226
|
+
route: `${firstPartyCollectPrefix}/**`,
|
|
1227
|
+
handler: proxyHandlerPath
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
801
1230
|
const scripts = await registry(resolvePath);
|
|
802
1231
|
for (const script of scripts) {
|
|
803
1232
|
if (script.import?.name) {
|
|
@@ -825,6 +1254,90 @@ const module$1 = defineNuxtModule({
|
|
|
825
1254
|
});
|
|
826
1255
|
}
|
|
827
1256
|
const { renderedScript } = setupPublicAssetStrategy(config.assets);
|
|
1257
|
+
if (firstPartyEnabled) {
|
|
1258
|
+
const proxyConfigs = getAllProxyConfigs(firstPartyCollectPrefix);
|
|
1259
|
+
const registryKeys = Object.keys(config.registry || {});
|
|
1260
|
+
const neededRoutes = {};
|
|
1261
|
+
const routePrivacyOverrides = {};
|
|
1262
|
+
const unsupportedScripts = [];
|
|
1263
|
+
for (const key of registryKeys) {
|
|
1264
|
+
const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
|
|
1265
|
+
const proxyKey = script?.proxy || void 0;
|
|
1266
|
+
if (proxyKey) {
|
|
1267
|
+
const proxyConfig = proxyConfigs[proxyKey];
|
|
1268
|
+
if (proxyConfig?.routes) {
|
|
1269
|
+
Object.assign(neededRoutes, proxyConfig.routes);
|
|
1270
|
+
for (const routePath of Object.keys(proxyConfig.routes)) {
|
|
1271
|
+
routePrivacyOverrides[routePath] = proxyConfig.privacy;
|
|
1272
|
+
}
|
|
1273
|
+
} else {
|
|
1274
|
+
unsupportedScripts.push(key);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (config.registry?.posthog && typeof config.registry.posthog === "object") {
|
|
1279
|
+
const phConfig = config.registry.posthog;
|
|
1280
|
+
if (!phConfig.apiHost) {
|
|
1281
|
+
const region = phConfig.region || "us";
|
|
1282
|
+
phConfig.apiHost = region === "eu" ? `${firstPartyCollectPrefix}/ph-eu` : `${firstPartyCollectPrefix}/ph`;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
if (unsupportedScripts.length && nuxt.options.dev) {
|
|
1286
|
+
logger.warn(
|
|
1287
|
+
`First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
|
|
1288
|
+
They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
const flatRoutes = {};
|
|
1292
|
+
for (const [path, config2] of Object.entries(neededRoutes)) {
|
|
1293
|
+
flatRoutes[path] = config2.proxy;
|
|
1294
|
+
}
|
|
1295
|
+
const allRewrites = [];
|
|
1296
|
+
for (const key of registryKeys) {
|
|
1297
|
+
const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
|
|
1298
|
+
const proxyKey = script?.proxy !== false ? script?.proxy || key : void 0;
|
|
1299
|
+
if (proxyKey) {
|
|
1300
|
+
const proxyConfig = proxyConfigs[proxyKey];
|
|
1301
|
+
if (proxyConfig?.rewrite) {
|
|
1302
|
+
allRewrites.push(...proxyConfig.rewrite);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
|
|
1307
|
+
routes: flatRoutes,
|
|
1308
|
+
privacy: firstPartyPrivacy,
|
|
1309
|
+
// undefined = use per-script defaults, set = global override
|
|
1310
|
+
routePrivacy: routePrivacyOverrides,
|
|
1311
|
+
// per-script privacy from registry
|
|
1312
|
+
rewrites: allRewrites
|
|
1313
|
+
};
|
|
1314
|
+
if (Object.keys(neededRoutes).length) {
|
|
1315
|
+
if (nuxt.options.dev) {
|
|
1316
|
+
const routeCount = Object.keys(neededRoutes).length;
|
|
1317
|
+
const scriptsCount = registryKeys.length;
|
|
1318
|
+
const privacyLabel = firstPartyPrivacy === void 0 ? "per-script" : typeof firstPartyPrivacy === "boolean" ? firstPartyPrivacy ? "anonymize" : "passthrough" : "custom";
|
|
1319
|
+
logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${privacyLabel})`);
|
|
1320
|
+
if (logger.level >= 4) {
|
|
1321
|
+
for (const [path, config2] of Object.entries(neededRoutes)) {
|
|
1322
|
+
logger.debug(` ${path} \u2192 ${config2.proxy}`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
if (isStaticPreset) {
|
|
1328
|
+
logger.warn(
|
|
1329
|
+
`First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
|
|
1330
|
+
Scripts will be bundled, but collection requests will not be proxied.
|
|
1331
|
+
|
|
1332
|
+
Options:
|
|
1333
|
+
1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
|
|
1334
|
+
2. Switch to server-rendered mode (ssr: true)
|
|
1335
|
+
3. Disable with firstParty: false
|
|
1336
|
+
|
|
1337
|
+
See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
828
1341
|
const moduleInstallPromises = /* @__PURE__ */ new Map();
|
|
829
1342
|
addBuildPlugin(NuxtScriptsCheckScripts(), {
|
|
830
1343
|
dev: true
|
|
@@ -832,12 +1345,14 @@ const module$1 = defineNuxtModule({
|
|
|
832
1345
|
addBuildPlugin(NuxtScriptBundleTransformer({
|
|
833
1346
|
scripts: registryScriptsWithImport,
|
|
834
1347
|
registryConfig: nuxt.options.runtimeConfig.public.scripts,
|
|
835
|
-
defaultBundle: config.defaultScriptOptions?.bundle,
|
|
1348
|
+
defaultBundle: firstPartyEnabled || config.defaultScriptOptions?.bundle,
|
|
1349
|
+
firstPartyEnabled,
|
|
1350
|
+
firstPartyCollectPrefix,
|
|
836
1351
|
moduleDetected(module) {
|
|
837
1352
|
if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
|
|
838
1353
|
moduleInstallPromises.set(module, () => installNuxtModule(module));
|
|
839
1354
|
},
|
|
840
|
-
assetsBaseURL:
|
|
1355
|
+
assetsBaseURL: assetsPrefix,
|
|
841
1356
|
fallbackOnSrcOnBundleFail: config.assets?.fallbackOnSrcOnBundleFail,
|
|
842
1357
|
fetchOptions: config.assets?.fetchOptions,
|
|
843
1358
|
cacheMaxAge: config.assets?.cacheMaxAge,
|
|
@@ -856,8 +1371,29 @@ const module$1 = defineNuxtModule({
|
|
|
856
1371
|
handler: await resolvePath("./runtime/server/google-static-maps-proxy")
|
|
857
1372
|
});
|
|
858
1373
|
}
|
|
859
|
-
|
|
1374
|
+
addServerHandler({
|
|
1375
|
+
route: "/api/_scripts/x-embed",
|
|
1376
|
+
handler: await resolvePath("./runtime/server/x-embed")
|
|
1377
|
+
});
|
|
1378
|
+
addServerHandler({
|
|
1379
|
+
route: "/api/_scripts/x-embed-image",
|
|
1380
|
+
handler: await resolvePath("./runtime/server/x-embed-image")
|
|
1381
|
+
});
|
|
1382
|
+
addServerHandler({
|
|
1383
|
+
route: "/api/_scripts/instagram-embed",
|
|
1384
|
+
handler: await resolvePath("./runtime/server/instagram-embed")
|
|
1385
|
+
});
|
|
1386
|
+
addServerHandler({
|
|
1387
|
+
route: "/api/_scripts/instagram-embed-image",
|
|
1388
|
+
handler: await resolvePath("./runtime/server/instagram-embed-image")
|
|
1389
|
+
});
|
|
1390
|
+
addServerHandler({
|
|
1391
|
+
route: "/api/_scripts/instagram-embed-asset",
|
|
1392
|
+
handler: await resolvePath("./runtime/server/instagram-embed-asset")
|
|
1393
|
+
});
|
|
1394
|
+
if (nuxt.options.dev) {
|
|
860
1395
|
setupDevToolsUI(config, resolvePath);
|
|
1396
|
+
}
|
|
861
1397
|
}
|
|
862
1398
|
});
|
|
863
1399
|
|