@nuxt/scripts 0.13.2 → 1.0.0-beta.2
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 +15 -0
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/B66N9HCo.js +1 -0
- package/dist/client/_nuxt/B8XOar-X.js +162 -0
- package/dist/client/_nuxt/{Bje-0OHL.js → DfLgoB--.js} +1 -1
- package/dist/client/_nuxt/DvH517bE.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/133a46c5-a5c1-4a63-87d1-037947a5bcdb.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 +106 -4
- package/dist/module.json +1 -1
- package/dist/module.mjs +705 -173
- package/dist/registry.mjs +63 -0
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +29 -1
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +35 -10
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +29 -1
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.d.vue.ts +20 -8
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +20 -8
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue +7 -1
- package/dist/runtime/components/ScriptCrisp.d.vue.ts +1 -1
- package/dist/runtime/components/ScriptCrisp.vue.d.ts +1 -1
- 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/ScriptIntercom.d.vue.ts +1 -1
- package/dist/runtime/components/ScriptIntercom.vue.d.ts +1 -1
- package/dist/runtime/components/ScriptVimeoPlayer.d.vue.ts +2 -2
- package/dist/runtime/components/ScriptVimeoPlayer.vue.d.ts +2 -2
- 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/components/ScriptYouTubePlayer.d.vue.ts +12 -1
- package/dist/runtime/components/ScriptYouTubePlayer.vue +41 -16
- package/dist/runtime/components/ScriptYouTubePlayer.vue.d.ts +12 -1
- package/dist/runtime/composables/useScript.js +34 -3
- package/dist/runtime/composables/useScriptTriggerServiceWorker.d.ts +7 -0
- package/dist/runtime/composables/useScriptTriggerServiceWorker.js +39 -0
- package/dist/runtime/npm-script-stub.d.ts +20 -0
- package/dist/runtime/npm-script-stub.js +73 -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/google-recaptcha.d.ts +27 -0
- package/dist/runtime/registry/google-recaptcha.js +45 -0
- package/dist/runtime/registry/google-sign-in.d.ts +84 -0
- package/dist/runtime/registry/google-sign-in.js +50 -0
- package/dist/runtime/registry/google-tag-manager.d.ts +3 -1
- package/dist/runtime/registry/google-tag-manager.js +15 -5
- 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/matomo-analytics.js +1 -1
- package/dist/runtime/registry/plausible-analytics.js +8 -6
- package/dist/runtime/registry/posthog.d.ts +26 -0
- package/dist/runtime/registry/posthog.js +92 -0
- package/dist/runtime/registry/rybbit-analytics.js +38 -8
- package/dist/runtime/registry/tiktok-pixel.d.ts +44 -0
- package/dist/runtime/registry/tiktok-pixel.js +44 -0
- package/dist/runtime/registry/x-embed.d.ts +77 -0
- package/dist/runtime/registry/x-embed.js +41 -0
- package/dist/runtime/server/google-static-maps-proxy.d.ts +2 -0
- package/dist/runtime/server/google-static-maps-proxy.js +54 -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 +230 -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 +97 -0
- package/dist/runtime/server/utils/privacy.js +268 -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 +42 -1
- package/dist/runtime/utils/pure.d.ts +13 -0
- package/dist/runtime/utils/pure.js +67 -0
- package/dist/runtime/utils.d.ts +3 -2
- package/dist/runtime/utils.js +11 -1
- package/dist/types.d.mts +1 -1
- package/package.json +39 -32
- package/dist/client/_nuxt/DMut0W-e.js +0 -162
- package/dist/client/_nuxt/builds/meta/5e0206fe-a683-423c-8d59-2596d0b16fee.json +0 -1
- package/dist/client/_nuxt/entry.BjfcJo5q.css +0 -1
- package/dist/client/_nuxt/error-404.B0ZhSNwd.css +0 -1
- package/dist/client/_nuxt/error-500.D4MdgPaC.css +0 -1
- package/dist/client/_nuxt/iNmKC7TZ.js +0 -1
- package/dist/client/_nuxt/rttsH3SL.js +0 -1
package/dist/module.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, tryUseNuxt, logger as logger$1, defineNuxtModule, createResolver, addImports, addComponentsDir, addTemplate,
|
|
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';
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
5
7
|
import fsp from 'node:fs/promises';
|
|
6
8
|
import { createUnplugin } from 'unplugin';
|
|
7
9
|
import MagicString from 'magic-string';
|
|
8
|
-
import {
|
|
10
|
+
import { parseAndWalk } from 'oxc-walker';
|
|
9
11
|
import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
|
|
10
12
|
import { hash } from 'ohash';
|
|
11
13
|
import { join, resolve, relative } from 'pathe';
|
|
@@ -14,6 +16,7 @@ import { fetch, $fetch } from 'ofetch';
|
|
|
14
16
|
import { lazyEventHandler, eventHandler, createError } from 'h3';
|
|
15
17
|
import { createStorage } from 'unstorage';
|
|
16
18
|
import fsDriver from 'unstorage/drivers/fs-lite';
|
|
19
|
+
import { rewriteScriptUrls } from '../dist/runtime/utils/pure.js';
|
|
17
20
|
import { pathToFileURL } from 'node:url';
|
|
18
21
|
import { isCI, provider } from 'std-env';
|
|
19
22
|
import { registry } from './registry.mjs';
|
|
@@ -44,20 +47,18 @@ async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
|
|
|
44
47
|
};
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
});
|
|
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
|
+
}
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -85,6 +86,9 @@ function setupPublicAssetStrategy(options = {}) {
|
|
|
85
86
|
const scriptDescriptor = renderedScript.get(join(assetsBaseURL, event.path.slice(1)));
|
|
86
87
|
if (!scriptDescriptor || scriptDescriptor instanceof Error)
|
|
87
88
|
throw createError({ statusCode: 404 });
|
|
89
|
+
if (scriptDescriptor.content) {
|
|
90
|
+
return scriptDescriptor.content;
|
|
91
|
+
}
|
|
88
92
|
const key = `bundle:${filename}`;
|
|
89
93
|
let res = await storage.getItemRaw(key);
|
|
90
94
|
if (!res) {
|
|
@@ -96,31 +100,204 @@ function setupPublicAssetStrategy(options = {}) {
|
|
|
96
100
|
})
|
|
97
101
|
});
|
|
98
102
|
if (nuxt.options.dev) {
|
|
99
|
-
|
|
100
|
-
nuxt.options.routeRules[joinURL(assetsBaseURL, "**")] = {
|
|
103
|
+
extendRouteRules(joinURL(assetsBaseURL, "**"), {
|
|
101
104
|
cache: {
|
|
102
105
|
maxAge: ONE_YEAR_IN_SECONDS
|
|
103
106
|
}
|
|
104
|
-
};
|
|
107
|
+
});
|
|
105
108
|
}
|
|
106
|
-
nuxt.options.nitro.publicAssets ||= [];
|
|
107
109
|
const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
|
|
108
|
-
nuxt.
|
|
109
|
-
|
|
110
|
-
publicAssets
|
|
110
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
111
|
+
nitroConfig.publicAssets ||= [];
|
|
112
|
+
nitroConfig.publicAssets.push({
|
|
111
113
|
dir: cacheDir,
|
|
112
114
|
maxAge: ONE_YEAR_IN_SECONDS,
|
|
113
115
|
baseURL: assetsBaseURL
|
|
114
|
-
}
|
|
115
|
-
prerender
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
});
|
|
117
|
+
nitroConfig.prerender ||= {};
|
|
118
|
+
nitroConfig.prerender.ignore ||= [];
|
|
119
|
+
nitroConfig.prerender.ignore.push(assetsBaseURL);
|
|
118
120
|
});
|
|
119
121
|
return {
|
|
120
122
|
renderedScript
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
|
|
126
|
+
function buildProxyConfig(collectPrefix) {
|
|
127
|
+
return {
|
|
128
|
+
googleAnalytics: {
|
|
129
|
+
rewrite: [
|
|
130
|
+
// Modern gtag.js uses www.google.com/g/collect
|
|
131
|
+
{ from: "www.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
132
|
+
// Older gtag.js constructs URLs dynamically: "https://"+(subdomain)+".google-analytics.com/g/collect"
|
|
133
|
+
// We need to catch the suffix pattern with leading dot
|
|
134
|
+
{ from: ".google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
135
|
+
{ from: ".analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
136
|
+
// Full domain patterns for static URLs
|
|
137
|
+
{ from: "www.google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
138
|
+
{ from: "analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
139
|
+
// Legacy endpoints still used by some scripts
|
|
140
|
+
{ from: "www.google-analytics.com", to: `${collectPrefix}/ga` },
|
|
141
|
+
{ from: "analytics.google.com", to: `${collectPrefix}/ga` },
|
|
142
|
+
// DoubleClick tracking (used by GA4 for ads/conversions)
|
|
143
|
+
{ from: "stats.g.doubleclick.net/g/collect", to: `${collectPrefix}/ga/g/collect` },
|
|
144
|
+
{ from: "stats.g.doubleclick.net", to: `${collectPrefix}/ga-dc` },
|
|
145
|
+
// Google Ads/Syndication tracking
|
|
146
|
+
{ from: "pagead2.googlesyndication.com", to: `${collectPrefix}/ga-syn` },
|
|
147
|
+
{ from: "www.googleadservices.com", to: `${collectPrefix}/ga-ads` },
|
|
148
|
+
{ from: "googleads.g.doubleclick.net", to: `${collectPrefix}/ga-gads` }
|
|
149
|
+
],
|
|
150
|
+
routes: {
|
|
151
|
+
[`${collectPrefix}/ga/**`]: { proxy: "https://www.google-analytics.com/**" },
|
|
152
|
+
[`${collectPrefix}/ga-dc/**`]: { proxy: "https://stats.g.doubleclick.net/**" },
|
|
153
|
+
[`${collectPrefix}/ga-syn/**`]: { proxy: "https://pagead2.googlesyndication.com/**" },
|
|
154
|
+
[`${collectPrefix}/ga-ads/**`]: { proxy: "https://www.googleadservices.com/**" },
|
|
155
|
+
[`${collectPrefix}/ga-gads/**`]: { proxy: "https://googleads.g.doubleclick.net/**" }
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
googleTagManager: {
|
|
159
|
+
rewrite: [
|
|
160
|
+
{ from: "www.googletagmanager.com", to: `${collectPrefix}/gtm` }
|
|
161
|
+
],
|
|
162
|
+
routes: {
|
|
163
|
+
[`${collectPrefix}/gtm/**`]: { proxy: "https://www.googletagmanager.com/**" }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
metaPixel: {
|
|
167
|
+
rewrite: [
|
|
168
|
+
// SDK script loading
|
|
169
|
+
{ from: "connect.facebook.net", to: `${collectPrefix}/meta` },
|
|
170
|
+
// Tracking pixel endpoint (www and non-www)
|
|
171
|
+
{ from: "www.facebook.com/tr", to: `${collectPrefix}/meta-tr` },
|
|
172
|
+
{ from: "facebook.com/tr", to: `${collectPrefix}/meta-tr` },
|
|
173
|
+
// Additional Meta tracking domains
|
|
174
|
+
{ from: "pixel.facebook.com", to: `${collectPrefix}/meta-px` },
|
|
175
|
+
{ from: "www.facebook.com/plugins", to: `${collectPrefix}/meta-plugins` }
|
|
176
|
+
],
|
|
177
|
+
routes: {
|
|
178
|
+
[`${collectPrefix}/meta/**`]: { proxy: "https://connect.facebook.net/**" },
|
|
179
|
+
[`${collectPrefix}/meta-tr/**`]: { proxy: "https://www.facebook.com/tr/**" },
|
|
180
|
+
[`${collectPrefix}/meta-px/**`]: { proxy: "https://pixel.facebook.com/**" },
|
|
181
|
+
[`${collectPrefix}/meta-plugins/**`]: { proxy: "https://www.facebook.com/plugins/**" }
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
tiktokPixel: {
|
|
185
|
+
rewrite: [
|
|
186
|
+
{ from: "analytics.tiktok.com", to: `${collectPrefix}/tiktok` }
|
|
187
|
+
],
|
|
188
|
+
routes: {
|
|
189
|
+
[`${collectPrefix}/tiktok/**`]: { proxy: "https://analytics.tiktok.com/**" }
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
segment: {
|
|
193
|
+
rewrite: [
|
|
194
|
+
{ from: "api.segment.io", to: `${collectPrefix}/segment` },
|
|
195
|
+
{ from: "cdn.segment.com", to: `${collectPrefix}/segment-cdn` }
|
|
196
|
+
],
|
|
197
|
+
routes: {
|
|
198
|
+
[`${collectPrefix}/segment/**`]: { proxy: "https://api.segment.io/**" },
|
|
199
|
+
[`${collectPrefix}/segment-cdn/**`]: { proxy: "https://cdn.segment.com/**" }
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
xPixel: {
|
|
203
|
+
rewrite: [
|
|
204
|
+
{ from: "analytics.twitter.com", to: `${collectPrefix}/x` },
|
|
205
|
+
{ from: "t.co", to: `${collectPrefix}/x-t` }
|
|
206
|
+
],
|
|
207
|
+
routes: {
|
|
208
|
+
[`${collectPrefix}/x/**`]: { proxy: "https://analytics.twitter.com/**" },
|
|
209
|
+
[`${collectPrefix}/x-t/**`]: { proxy: "https://t.co/**" }
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
snapchatPixel: {
|
|
213
|
+
rewrite: [
|
|
214
|
+
{ from: "tr.snapchat.com", to: `${collectPrefix}/snap` }
|
|
215
|
+
],
|
|
216
|
+
routes: {
|
|
217
|
+
[`${collectPrefix}/snap/**`]: { proxy: "https://tr.snapchat.com/**" }
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
redditPixel: {
|
|
221
|
+
rewrite: [
|
|
222
|
+
{ from: "alb.reddit.com", to: `${collectPrefix}/reddit` }
|
|
223
|
+
],
|
|
224
|
+
routes: {
|
|
225
|
+
[`${collectPrefix}/reddit/**`]: { proxy: "https://alb.reddit.com/**" }
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
clarity: {
|
|
229
|
+
rewrite: [
|
|
230
|
+
// Main clarity domain
|
|
231
|
+
{ from: "www.clarity.ms", to: `${collectPrefix}/clarity` },
|
|
232
|
+
// Script loader (the actual SDK is loaded from here)
|
|
233
|
+
{ from: "scripts.clarity.ms", to: `${collectPrefix}/clarity-scripts` },
|
|
234
|
+
// Data collection endpoint
|
|
235
|
+
{ from: "d.clarity.ms", to: `${collectPrefix}/clarity-data` },
|
|
236
|
+
// Event collection endpoint
|
|
237
|
+
{ from: "e.clarity.ms", to: `${collectPrefix}/clarity-events` }
|
|
238
|
+
],
|
|
239
|
+
routes: {
|
|
240
|
+
[`${collectPrefix}/clarity/**`]: { proxy: "https://www.clarity.ms/**" },
|
|
241
|
+
[`${collectPrefix}/clarity-scripts/**`]: { proxy: "https://scripts.clarity.ms/**" },
|
|
242
|
+
[`${collectPrefix}/clarity-data/**`]: { proxy: "https://d.clarity.ms/**" },
|
|
243
|
+
[`${collectPrefix}/clarity-events/**`]: { proxy: "https://e.clarity.ms/**" }
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
hotjar: {
|
|
247
|
+
rewrite: [
|
|
248
|
+
// Static assets
|
|
249
|
+
{ from: "static.hotjar.com", to: `${collectPrefix}/hotjar` },
|
|
250
|
+
// Script loader (bootstrap script loads the main SDK from here)
|
|
251
|
+
{ from: "script.hotjar.com", to: `${collectPrefix}/hotjar-script` },
|
|
252
|
+
// Configuration/variables
|
|
253
|
+
{ from: "vars.hotjar.com", to: `${collectPrefix}/hotjar-vars` },
|
|
254
|
+
// Data ingestion endpoint
|
|
255
|
+
{ from: "in.hotjar.com", to: `${collectPrefix}/hotjar-in` },
|
|
256
|
+
// Video capture
|
|
257
|
+
{ from: "vc.hotjar.com", to: `${collectPrefix}/hotjar-vc` },
|
|
258
|
+
// Metrics/telemetry
|
|
259
|
+
{ from: "metrics.hotjar.io", to: `${collectPrefix}/hotjar-metrics` },
|
|
260
|
+
// Insights (ContentSquare integration)
|
|
261
|
+
{ from: "insights.hotjar.com", to: `${collectPrefix}/hotjar-insights` }
|
|
262
|
+
],
|
|
263
|
+
routes: {
|
|
264
|
+
[`${collectPrefix}/hotjar/**`]: { proxy: "https://static.hotjar.com/**" },
|
|
265
|
+
[`${collectPrefix}/hotjar-script/**`]: { proxy: "https://script.hotjar.com/**" },
|
|
266
|
+
[`${collectPrefix}/hotjar-vars/**`]: { proxy: "https://vars.hotjar.com/**" },
|
|
267
|
+
[`${collectPrefix}/hotjar-in/**`]: { proxy: "https://in.hotjar.com/**" },
|
|
268
|
+
[`${collectPrefix}/hotjar-vc/**`]: { proxy: "https://vc.hotjar.com/**" },
|
|
269
|
+
[`${collectPrefix}/hotjar-metrics/**`]: { proxy: "https://metrics.hotjar.io/**" },
|
|
270
|
+
[`${collectPrefix}/hotjar-insights/**`]: { proxy: "https://insights.hotjar.com/**" }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function getProxyConfig(key, collectPrefix) {
|
|
276
|
+
const configs = buildProxyConfig(collectPrefix);
|
|
277
|
+
return configs[key];
|
|
278
|
+
}
|
|
279
|
+
function getAllProxyConfigs(collectPrefix) {
|
|
280
|
+
return buildProxyConfig(collectPrefix);
|
|
281
|
+
}
|
|
282
|
+
function getSWInterceptRules(collectPrefix) {
|
|
283
|
+
const configs = buildProxyConfig(collectPrefix);
|
|
284
|
+
const rules = [];
|
|
285
|
+
for (const config of Object.values(configs)) {
|
|
286
|
+
if (!config.routes)
|
|
287
|
+
continue;
|
|
288
|
+
for (const [localPath, { proxy }] of Object.entries(config.routes)) {
|
|
289
|
+
const match = proxy.match(/^https?:\/\/([^/]+)(\/.*)?\/\*\*$/);
|
|
290
|
+
if (match?.[1]) {
|
|
291
|
+
const domain = match[1];
|
|
292
|
+
const pathPrefix = match[2] || "";
|
|
293
|
+
const target = localPath.replace(/\/\*\*$/, "");
|
|
294
|
+
rules.push({ pattern: domain, pathPrefix, target });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return rules;
|
|
299
|
+
}
|
|
300
|
+
|
|
124
301
|
function isVue(id, opts = {}) {
|
|
125
302
|
const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
|
|
126
303
|
if (id.endsWith(".vue") && !search) {
|
|
@@ -149,6 +326,10 @@ function isJS(id) {
|
|
|
149
326
|
}
|
|
150
327
|
|
|
151
328
|
const SEVEN_DAYS_IN_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
329
|
+
function calculateIntegrity(content, algorithm = "sha384") {
|
|
330
|
+
const hash = createHash(algorithm).update(content).digest("base64");
|
|
331
|
+
return `${algorithm}-${hash}`;
|
|
332
|
+
}
|
|
152
333
|
async function isCacheExpired(storage, filename, cacheMaxAge = SEVEN_DAYS_IN_MS) {
|
|
153
334
|
const metaKey = `bundle-meta:${filename}`;
|
|
154
335
|
const meta = await storage.getItem(metaKey);
|
|
@@ -173,7 +354,7 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
|
|
|
173
354
|
return { url: src };
|
|
174
355
|
}
|
|
175
356
|
async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
176
|
-
const { src, url, filename, forceDownload } = opts;
|
|
357
|
+
const { src, url, filename, forceDownload, integrity, proxyRewrites } = opts;
|
|
177
358
|
if (src === url || !filename) {
|
|
178
359
|
return;
|
|
179
360
|
}
|
|
@@ -181,16 +362,19 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
|
181
362
|
const scriptContent = renderedScript.get(src);
|
|
182
363
|
let res = scriptContent instanceof Error ? void 0 : scriptContent?.content;
|
|
183
364
|
if (!res) {
|
|
184
|
-
const
|
|
365
|
+
const proxyRewritesHash = proxyRewrites?.length ? `-${hash(proxyRewrites)}` : "";
|
|
366
|
+
const cacheKey = proxyRewrites?.length ? `bundle-proxy:${filename.replace(".js", `${proxyRewritesHash}.js`)}` : `bundle:${filename}`;
|
|
185
367
|
const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !await isCacheExpired(storage, filename, cacheMaxAge);
|
|
186
368
|
if (shouldUseCache) {
|
|
187
|
-
const
|
|
369
|
+
const cachedContent = await storage.getItemRaw(cacheKey);
|
|
370
|
+
const meta = await storage.getItem(`bundle-meta:${filename}`);
|
|
188
371
|
renderedScript.set(url, {
|
|
189
|
-
content:
|
|
190
|
-
size:
|
|
372
|
+
content: cachedContent,
|
|
373
|
+
size: cachedContent.length / 1024,
|
|
191
374
|
encoding: "utf-8",
|
|
192
375
|
src,
|
|
193
|
-
filename
|
|
376
|
+
filename,
|
|
377
|
+
integrity: meta?.integrity
|
|
194
378
|
});
|
|
195
379
|
return;
|
|
196
380
|
}
|
|
@@ -206,19 +390,29 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
|
|
|
206
390
|
return Buffer.from(r._data || await r.arrayBuffer());
|
|
207
391
|
});
|
|
208
392
|
await storage.setItemRaw(`bundle:${filename}`, res);
|
|
393
|
+
if (proxyRewrites?.length && res) {
|
|
394
|
+
const content = res.toString("utf-8");
|
|
395
|
+
const rewritten = rewriteScriptUrls(content, proxyRewrites);
|
|
396
|
+
res = Buffer.from(rewritten, "utf-8");
|
|
397
|
+
logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
|
|
398
|
+
}
|
|
399
|
+
const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
|
|
400
|
+
await storage.setItemRaw(cacheKey, res);
|
|
209
401
|
await storage.setItem(`bundle-meta:${filename}`, {
|
|
210
402
|
timestamp: Date.now(),
|
|
211
403
|
src,
|
|
212
|
-
filename
|
|
404
|
+
filename,
|
|
405
|
+
integrity: integrityHash
|
|
213
406
|
});
|
|
214
407
|
size = size || res.length / 1024;
|
|
215
|
-
logger.info(`Downloading script ${colors.gray(`${src} \u2192 ${filename} (${size.toFixed(2)} kB ${encoding})`)}`);
|
|
408
|
+
logger.info(`Downloading script ${colors.gray(`${src} \u2192 ${filename} (${size.toFixed(2)} kB ${encoding})${integrityHash ? ` [${integrityHash.slice(0, 15)}...]` : ""}`)}`);
|
|
216
409
|
renderedScript.set(url, {
|
|
217
410
|
content: res,
|
|
218
411
|
size,
|
|
219
412
|
encoding,
|
|
220
413
|
src,
|
|
221
|
-
filename
|
|
414
|
+
filename,
|
|
415
|
+
integrity: integrityHash
|
|
222
416
|
});
|
|
223
417
|
}
|
|
224
418
|
}
|
|
@@ -252,16 +446,21 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
252
446
|
return createUnplugin(() => {
|
|
253
447
|
return {
|
|
254
448
|
name: "nuxt:scripts:bundler-transformer",
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
449
|
+
transform: {
|
|
450
|
+
filter: {
|
|
451
|
+
id: {
|
|
452
|
+
include: [/\.vue/, /\.[cm]?[jt]sx?$/],
|
|
453
|
+
exclude: [/\.(?:test|spec)\./]
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
async handler(code, id) {
|
|
457
|
+
if (!isVue(id, { type: ["template", "script"] }) && !isJS(id))
|
|
458
|
+
return;
|
|
459
|
+
if (!code.includes("useScript"))
|
|
460
|
+
return;
|
|
461
|
+
const s = new MagicString(code);
|
|
462
|
+
const deferredOps = [];
|
|
463
|
+
parseAndWalk(code, id, function(_node) {
|
|
265
464
|
const calleeName = _node.callee?.name;
|
|
266
465
|
if (!calleeName)
|
|
267
466
|
return;
|
|
@@ -271,6 +470,11 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
271
470
|
const node = _node;
|
|
272
471
|
let scriptSrcNode;
|
|
273
472
|
let src;
|
|
473
|
+
let registryKey;
|
|
474
|
+
if (fnName !== "useScript") {
|
|
475
|
+
const baseName = fnName.replace(/^useScript/, "");
|
|
476
|
+
registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : void 0;
|
|
477
|
+
}
|
|
274
478
|
if (fnName === "useScript") {
|
|
275
479
|
if (node.arguments[0]?.type === "Literal") {
|
|
276
480
|
scriptSrcNode = node.arguments[0];
|
|
@@ -287,9 +491,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
287
491
|
}
|
|
288
492
|
if (!registryNode.scriptBundling && !registryNode.src)
|
|
289
493
|
return;
|
|
290
|
-
const
|
|
291
|
-
const registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : "";
|
|
292
|
-
const registryConfig = options.registryConfig?.[registryKey] || {};
|
|
494
|
+
const registryConfig = options.registryConfig?.[registryKey || ""] || {};
|
|
293
495
|
const fnArg0 = {};
|
|
294
496
|
if (node.arguments[0]?.type === "ObjectExpression") {
|
|
295
497
|
const optionsNode = node.arguments[0];
|
|
@@ -309,6 +511,8 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
309
511
|
src = registryNode.scriptBundling && registryNode.scriptBundling(mergedOptions);
|
|
310
512
|
if (src === false)
|
|
311
513
|
return;
|
|
514
|
+
if (!src && registryNode.src)
|
|
515
|
+
src = registryNode.src;
|
|
312
516
|
}
|
|
313
517
|
}
|
|
314
518
|
if (!scriptSrcNode && !src) {
|
|
@@ -323,8 +527,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
323
527
|
if (bundleProperty && bundleProperty.value.type === "Literal") {
|
|
324
528
|
const bundleValue = bundleProperty.value.value;
|
|
325
529
|
if (bundleValue === true || bundleValue === "force" || String(bundleValue) === "true") {
|
|
326
|
-
|
|
327
|
-
s.overwrite(valueNode.start, valueNode.end, `'unsupported'`);
|
|
530
|
+
s.overwrite(bundleProperty.value.start, bundleProperty.value.end, `'unsupported'`);
|
|
328
531
|
}
|
|
329
532
|
}
|
|
330
533
|
}
|
|
@@ -341,8 +544,7 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
341
544
|
(p) => (p.key?.name === "bundle" || p.key?.value === "bundle") && p.type === "Property"
|
|
342
545
|
);
|
|
343
546
|
if (bundleProperty && bundleProperty.value.type === "Literal") {
|
|
344
|
-
const
|
|
345
|
-
const bundleValue = value.value;
|
|
547
|
+
const bundleValue = bundleProperty.value.value;
|
|
346
548
|
if (bundleValue !== true && bundleValue !== "force" && String(bundleValue) !== "true") {
|
|
347
549
|
canBundle = false;
|
|
348
550
|
return;
|
|
@@ -370,67 +572,104 @@ function NuxtScriptBundleTransformer(options = {
|
|
|
370
572
|
canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
|
|
371
573
|
forceDownload = bundleValue === "force";
|
|
372
574
|
}
|
|
575
|
+
const firstPartyOption = scriptOptions?.value.properties?.find((prop) => {
|
|
576
|
+
return prop.type === "Property" && prop.key?.name === "firstParty" && prop.value.type === "Literal";
|
|
577
|
+
});
|
|
578
|
+
let firstPartyOptOut = firstPartyOption?.value.value === false;
|
|
579
|
+
if (!firstPartyOptOut && node.arguments[1]?.type === "ObjectExpression") {
|
|
580
|
+
const secondArgFirstPartyProp = node.arguments[1].properties.find(
|
|
581
|
+
(p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
|
|
582
|
+
);
|
|
583
|
+
firstPartyOptOut = secondArgFirstPartyProp?.value.value === false;
|
|
584
|
+
}
|
|
585
|
+
if (!firstPartyOptOut && node.arguments[0]?.type === "ObjectExpression") {
|
|
586
|
+
const firstArgFirstPartyProp = node.arguments[0].properties.find(
|
|
587
|
+
(p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
|
|
588
|
+
);
|
|
589
|
+
firstPartyOptOut = firstArgFirstPartyProp?.value.value === false;
|
|
590
|
+
}
|
|
373
591
|
if (canBundle) {
|
|
374
592
|
const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
url
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
593
|
+
const script = options.scripts?.find((s2) => s2.import.name === fnName);
|
|
594
|
+
const proxyConfigKey = script?.proxy !== false ? script?.proxy || registryKey : void 0;
|
|
595
|
+
const proxyRewrites = options.firstPartyEnabled && !firstPartyOptOut && proxyConfigKey && options.firstPartyCollectPrefix ? getProxyConfig(proxyConfigKey, options.firstPartyCollectPrefix)?.rewrite : void 0;
|
|
596
|
+
deferredOps.push(async () => {
|
|
597
|
+
let url = _url;
|
|
598
|
+
try {
|
|
599
|
+
await downloadScript({ src, url, filename, forceDownload, proxyRewrites, integrity: options.integrity }, renderedScript, options.fetchOptions, options.cacheMaxAge);
|
|
600
|
+
} catch (e) {
|
|
601
|
+
if (options.fallbackOnSrcOnBundleFail) {
|
|
602
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
|
|
603
|
+
url = src;
|
|
604
|
+
} else {
|
|
605
|
+
const errorMessage = e?.message || "Unknown error";
|
|
606
|
+
if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("certificate")) {
|
|
607
|
+
logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
|
|
608
|
+
logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
|
|
609
|
+
}
|
|
610
|
+
throw e;
|
|
387
611
|
}
|
|
388
|
-
throw e;
|
|
389
612
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
(
|
|
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
|
-
}
|
|
613
|
+
if (src === url) {
|
|
614
|
+
if (src && src.startsWith("/"))
|
|
615
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
|
|
616
|
+
else
|
|
617
|
+
logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
|
|
618
|
+
}
|
|
619
|
+
const scriptMeta = renderedScript.get(url);
|
|
620
|
+
const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
|
|
621
|
+
if (scriptSrcNode) {
|
|
622
|
+
if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
|
|
623
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
|
|
624
|
+
} else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
|
|
625
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
626
|
+
s.appendLeft(node.arguments[0].end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
|
|
416
627
|
} else {
|
|
417
|
-
s.
|
|
628
|
+
s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
|
|
418
629
|
}
|
|
419
630
|
} else {
|
|
420
|
-
|
|
631
|
+
const integrityProps = integrityHash ? `, integrity: '${integrityHash}', crossorigin: 'anonymous'` : "";
|
|
632
|
+
if (node.arguments[0]) {
|
|
633
|
+
const optionsNode = node.arguments[0];
|
|
634
|
+
const scriptInputProperty = optionsNode.properties.find(
|
|
635
|
+
(p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
|
|
636
|
+
);
|
|
637
|
+
if (scriptInputProperty) {
|
|
638
|
+
const scriptInput = scriptInputProperty.value;
|
|
639
|
+
if (scriptInput.type === "ObjectExpression") {
|
|
640
|
+
const srcProperty = scriptInput.properties.find(
|
|
641
|
+
(p) => p.key?.name === "src" || p.key?.value === "src"
|
|
642
|
+
);
|
|
643
|
+
if (srcProperty) {
|
|
644
|
+
s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
|
|
645
|
+
if (integrityHash)
|
|
646
|
+
s.appendLeft(scriptInput.end - 1, integrityProps);
|
|
647
|
+
} else {
|
|
648
|
+
s.appendRight(scriptInput.end - 1, `, src: '${url}'${integrityProps}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
|
|
653
|
+
}
|
|
654
|
+
} else {
|
|
655
|
+
s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
|
|
656
|
+
}
|
|
421
657
|
}
|
|
422
|
-
}
|
|
658
|
+
});
|
|
423
659
|
}
|
|
424
660
|
}
|
|
425
661
|
}
|
|
426
662
|
}
|
|
663
|
+
});
|
|
664
|
+
for (const op of deferredOps) {
|
|
665
|
+
await op();
|
|
666
|
+
}
|
|
667
|
+
if (s.hasChanged()) {
|
|
668
|
+
return {
|
|
669
|
+
code: s.toString(),
|
|
670
|
+
map: s.generateMap({ includeContent: true, source: id })
|
|
671
|
+
};
|
|
427
672
|
}
|
|
428
|
-
});
|
|
429
|
-
if (s.hasChanged()) {
|
|
430
|
-
return {
|
|
431
|
-
code: s.toString(),
|
|
432
|
-
map: s.generateMap({ includeContent: true, source: id })
|
|
433
|
-
};
|
|
434
673
|
}
|
|
435
674
|
}
|
|
436
675
|
};
|
|
@@ -481,17 +720,18 @@ function NuxtScriptsCheckScripts() {
|
|
|
481
720
|
return createUnplugin(() => {
|
|
482
721
|
return {
|
|
483
722
|
name: "nuxt-scripts:check-scripts",
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
723
|
+
transform: {
|
|
724
|
+
filter: {
|
|
725
|
+
id: /\.vue/
|
|
726
|
+
},
|
|
727
|
+
handler(code, id) {
|
|
728
|
+
if (!isVue(id, { type: ["script"] }))
|
|
729
|
+
return;
|
|
730
|
+
if (!code.includes("useScript"))
|
|
731
|
+
return;
|
|
732
|
+
let nameNode;
|
|
733
|
+
let errorNode;
|
|
734
|
+
parseAndWalk(code, id, function(_node) {
|
|
495
735
|
if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
|
|
496
736
|
const objPattern = _node.declarations[0]?.id;
|
|
497
737
|
for (const property of objPattern.properties) {
|
|
@@ -521,19 +761,58 @@ function NuxtScriptsCheckScripts() {
|
|
|
521
761
|
}
|
|
522
762
|
}
|
|
523
763
|
}
|
|
764
|
+
});
|
|
765
|
+
if (errorNode) {
|
|
766
|
+
return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
|
|
524
767
|
}
|
|
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
768
|
}
|
|
529
769
|
}
|
|
530
770
|
};
|
|
531
771
|
});
|
|
532
772
|
}
|
|
533
773
|
|
|
774
|
+
function registerTypeTemplates({ nuxt, config, newScripts }) {
|
|
775
|
+
addTypeTemplate({
|
|
776
|
+
filename: "types/nuxt-scripts-augments.d.ts",
|
|
777
|
+
getContents: () => {
|
|
778
|
+
const typesPath = relative(
|
|
779
|
+
resolve(nuxt.options.rootDir, nuxt.options.buildDir, "types"),
|
|
780
|
+
resolve("runtime/types")
|
|
781
|
+
);
|
|
782
|
+
let augments = `// Generated by @nuxt/scripts
|
|
783
|
+
declare module '#app' {
|
|
784
|
+
interface NuxtApp {
|
|
785
|
+
$scripts: Record<${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`).concat(["string"]).join(" | ")}, import('#nuxt-scripts/types').UseScriptContext<any> | undefined>
|
|
786
|
+
_scripts: Record<string, import('#nuxt-scripts/types').NuxtDevToolsScriptInstance>
|
|
787
|
+
}
|
|
788
|
+
interface RuntimeNuxtHooks {
|
|
789
|
+
'scripts:updated': (ctx: { scripts: Record<string, import('#nuxt-scripts/types').NuxtDevToolsScriptInstance> }) => void | Promise<void>
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
`;
|
|
793
|
+
if (newScripts.length) {
|
|
794
|
+
augments += `
|
|
795
|
+
declare module '#nuxt-scripts/types' {
|
|
796
|
+
type _NuxtScriptOptions = Omit<import('${typesPath}').NuxtUseScriptOptions, 'use' | 'beforeInit'>
|
|
797
|
+
interface ScriptRegistry {
|
|
798
|
+
${newScripts.map((i) => {
|
|
799
|
+
const key = i.import.name.replace("useScript", "");
|
|
800
|
+
const keyLcFirst = key.substring(0, 1).toLowerCase() + key.substring(1);
|
|
801
|
+
return ` ${keyLcFirst}?: import('${i.import.from}').${key}Input | [import('${i.import.from}').${key}Input, _NuxtScriptOptions]`;
|
|
802
|
+
}).join("\n")}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
`;
|
|
806
|
+
}
|
|
807
|
+
return `${augments}
|
|
808
|
+
export {}`;
|
|
809
|
+
}
|
|
810
|
+
}, { nuxt: true });
|
|
811
|
+
}
|
|
534
812
|
function templateTriggerResolver(defaultScriptOptions) {
|
|
535
813
|
const needsIdleTimeout = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "idleTimeout" in defaultScriptOptions.trigger;
|
|
536
814
|
const needsInteraction = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "interaction" in defaultScriptOptions.trigger;
|
|
815
|
+
const needsServiceWorker = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "serviceWorker" in defaultScriptOptions.trigger;
|
|
537
816
|
const imports = [];
|
|
538
817
|
if (needsIdleTimeout) {
|
|
539
818
|
imports.push(`import { useScriptTriggerIdleTimeout } from '#nuxt-scripts/composables/useScriptTriggerIdleTimeout'`);
|
|
@@ -541,11 +820,15 @@ function templateTriggerResolver(defaultScriptOptions) {
|
|
|
541
820
|
if (needsInteraction) {
|
|
542
821
|
imports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
|
|
543
822
|
}
|
|
823
|
+
if (needsServiceWorker) {
|
|
824
|
+
imports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
|
|
825
|
+
}
|
|
544
826
|
return [
|
|
545
827
|
...imports,
|
|
546
828
|
`export function resolveTrigger(trigger) {`,
|
|
547
829
|
needsIdleTimeout ? ` if ('idleTimeout' in trigger) return useScriptTriggerIdleTimeout({ timeout: trigger.idleTimeout })` : "",
|
|
548
830
|
needsInteraction ? ` if ('interaction' in trigger) return useScriptTriggerInteraction({ events: trigger.interaction })` : "",
|
|
831
|
+
needsServiceWorker ? ` if ('serviceWorker' in trigger) return useScriptTriggerServiceWorker()` : "",
|
|
549
832
|
` return null`,
|
|
550
833
|
`}`
|
|
551
834
|
].filter(Boolean).join("\n");
|
|
@@ -562,6 +845,9 @@ function resolveTriggerForTemplate(trigger) {
|
|
|
562
845
|
if ("interaction" in trigger) {
|
|
563
846
|
return `useScriptTriggerInteraction({ events: ${JSON.stringify(trigger.interaction)} })`;
|
|
564
847
|
}
|
|
848
|
+
if ("serviceWorker" in trigger) {
|
|
849
|
+
return `useScriptTriggerServiceWorker()`;
|
|
850
|
+
}
|
|
565
851
|
}
|
|
566
852
|
return null;
|
|
567
853
|
}
|
|
@@ -572,27 +858,34 @@ function templatePlugin(config, registry) {
|
|
|
572
858
|
}
|
|
573
859
|
const imports = [];
|
|
574
860
|
const inits = [];
|
|
861
|
+
const resolvedRegistryKeys = [];
|
|
575
862
|
let needsIdleTimeoutImport = false;
|
|
576
863
|
let needsInteractionImport = false;
|
|
864
|
+
let needsServiceWorkerImport = false;
|
|
577
865
|
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)}`);
|
|
866
|
+
const importDefinition = registry.find((i) => i.proxy === k || i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
|
|
579
867
|
if (importDefinition) {
|
|
868
|
+
resolvedRegistryKeys.push(k);
|
|
580
869
|
imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
|
|
581
|
-
const args = (typeof c !== "object" ? {} : c) || {};
|
|
582
870
|
if (c === "mock") {
|
|
583
|
-
|
|
584
|
-
} else if (Array.isArray(c) && c.length === 2
|
|
585
|
-
const
|
|
871
|
+
inits.push(`const ${k} = ${importDefinition.import.name}({ scriptOptions: { trigger: 'manual', skipValidation: true } })`);
|
|
872
|
+
} else if (Array.isArray(c) && c.length === 2) {
|
|
873
|
+
const input = c[0] || {};
|
|
874
|
+
const scriptOptions = { ...c[1] };
|
|
875
|
+
const triggerResolved = resolveTriggerForTemplate(scriptOptions?.trigger);
|
|
586
876
|
if (triggerResolved) {
|
|
587
|
-
|
|
588
|
-
if (args.scriptOptions) {
|
|
589
|
-
args.scriptOptions.trigger = `__TRIGGER_${triggerResolved}__`;
|
|
590
|
-
}
|
|
877
|
+
scriptOptions.trigger = "__TRIGGER_PLACEHOLDER__";
|
|
591
878
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
592
879
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
880
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
593
881
|
}
|
|
882
|
+
const args = { ...input, scriptOptions };
|
|
883
|
+
const argsJson = triggerResolved ? JSON.stringify(args).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved) : JSON.stringify(args);
|
|
884
|
+
inits.push(`const ${k} = ${importDefinition.import.name}(${argsJson})`);
|
|
885
|
+
} else {
|
|
886
|
+
const args = (typeof c !== "object" ? {} : c) || {};
|
|
887
|
+
inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args)})`);
|
|
594
888
|
}
|
|
595
|
-
inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args).replace(/"__TRIGGER_(.*?)__"/g, "$1")})`);
|
|
596
889
|
}
|
|
597
890
|
}
|
|
598
891
|
for (const [k, c] of Object.entries(config.globals || {})) {
|
|
@@ -604,8 +897,10 @@ function templatePlugin(config, registry) {
|
|
|
604
897
|
if (triggerResolved) {
|
|
605
898
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
606
899
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
607
|
-
|
|
608
|
-
|
|
900
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
901
|
+
const resolvedOptions = { ...options, trigger: "__TRIGGER_PLACEHOLDER__" };
|
|
902
|
+
const optionsJson = JSON.stringify(resolvedOptions).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
|
|
903
|
+
inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${optionsJson}, use: () => ({ ${k}: window.${k} }) })`);
|
|
609
904
|
} else {
|
|
610
905
|
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
906
|
}
|
|
@@ -614,8 +909,10 @@ function templatePlugin(config, registry) {
|
|
|
614
909
|
if (triggerResolved) {
|
|
615
910
|
if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
|
|
616
911
|
if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
|
|
617
|
-
|
|
618
|
-
|
|
912
|
+
if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
|
|
913
|
+
const resolvedOptions = { ...c, trigger: "__TRIGGER_PLACEHOLDER__" };
|
|
914
|
+
const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
|
|
915
|
+
inits.push(`const ${k} = useScript(${argsJson}, { use: () => ({ ${k}: window.${k} }) })`);
|
|
619
916
|
} else {
|
|
620
917
|
inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
|
|
621
918
|
}
|
|
@@ -628,6 +925,9 @@ function templatePlugin(config, registry) {
|
|
|
628
925
|
if (needsInteractionImport) {
|
|
629
926
|
triggerImports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
|
|
630
927
|
}
|
|
928
|
+
if (needsServiceWorkerImport) {
|
|
929
|
+
triggerImports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
|
|
930
|
+
}
|
|
631
931
|
return [
|
|
632
932
|
`import { useScript } from '#nuxt-scripts/composables/useScript'`,
|
|
633
933
|
`import { defineNuxtPlugin } from 'nuxt/app'`,
|
|
@@ -640,12 +940,26 @@ function templatePlugin(config, registry) {
|
|
|
640
940
|
` parallel: true,`,
|
|
641
941
|
` setup() {`,
|
|
642
942
|
...inits.map((i) => ` ${i}`),
|
|
643
|
-
` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...
|
|
943
|
+
` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
|
|
644
944
|
` }`,
|
|
645
945
|
`})`
|
|
646
946
|
].join("\n");
|
|
647
947
|
}
|
|
648
948
|
|
|
949
|
+
const PARTYTOWN_FORWARDS = {
|
|
950
|
+
googleAnalytics: ["dataLayer.push", "gtag"],
|
|
951
|
+
plausible: ["plausible"],
|
|
952
|
+
fathom: ["fathom", "fathom.trackEvent", "fathom.trackPageview"],
|
|
953
|
+
umami: ["umami", "umami.track"],
|
|
954
|
+
matomo: ["_paq.push"],
|
|
955
|
+
segment: ["analytics", "analytics.track", "analytics.page", "analytics.identify"],
|
|
956
|
+
metaPixel: ["fbq"],
|
|
957
|
+
xPixel: ["twq"],
|
|
958
|
+
tiktokPixel: ["ttq.track", "ttq.page", "ttq.identify"],
|
|
959
|
+
snapchatPixel: ["snaptr"],
|
|
960
|
+
redditPixel: ["rdt"],
|
|
961
|
+
cloudflareWebAnalytics: ["__cfBeacon"]
|
|
962
|
+
};
|
|
649
963
|
const module$1 = defineNuxtModule({
|
|
650
964
|
meta: {
|
|
651
965
|
name: "@nuxt/scripts",
|
|
@@ -655,6 +969,7 @@ const module$1 = defineNuxtModule({
|
|
|
655
969
|
}
|
|
656
970
|
},
|
|
657
971
|
defaults: {
|
|
972
|
+
firstParty: true,
|
|
658
973
|
defaultScriptOptions: {
|
|
659
974
|
trigger: "onNuxtReady"
|
|
660
975
|
},
|
|
@@ -668,6 +983,10 @@ const module$1 = defineNuxtModule({
|
|
|
668
983
|
// Configures the maximum time (in milliseconds) allowed for each fetch attempt.
|
|
669
984
|
}
|
|
670
985
|
},
|
|
986
|
+
googleStaticMapsProxy: {
|
|
987
|
+
enabled: false,
|
|
988
|
+
cacheMaxAge: 3600
|
|
989
|
+
},
|
|
671
990
|
enabled: true,
|
|
672
991
|
debug: false
|
|
673
992
|
},
|
|
@@ -687,11 +1006,18 @@ const module$1 = defineNuxtModule({
|
|
|
687
1006
|
if (unheadVersion?.startsWith("1")) {
|
|
688
1007
|
logger.error(`Nuxt Scripts requires Unhead >= 2, you are using v${unheadVersion}. Please run \`nuxi upgrade --clean\` to upgrade...`);
|
|
689
1008
|
}
|
|
690
|
-
nuxt.options.runtimeConfig["nuxt-scripts"] = {
|
|
1009
|
+
nuxt.options.runtimeConfig["nuxt-scripts"] = {
|
|
1010
|
+
version,
|
|
1011
|
+
// Private proxy config with API key (server-side only)
|
|
1012
|
+
googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0,
|
|
1013
|
+
swTemplate: readFileSync(await resolvePath("./runtime/sw/proxy-sw.template.js"), "utf-8")
|
|
1014
|
+
};
|
|
691
1015
|
nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
|
|
692
1016
|
// expose for devtools
|
|
693
1017
|
version: nuxt.options.dev ? version : void 0,
|
|
694
|
-
defaultScriptOptions: config.defaultScriptOptions
|
|
1018
|
+
defaultScriptOptions: config.defaultScriptOptions,
|
|
1019
|
+
// Only expose enabled and cacheMaxAge to client, not apiKey
|
|
1020
|
+
googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { enabled: true, cacheMaxAge: config.googleStaticMapsProxy.cacheMaxAge } : void 0
|
|
695
1021
|
};
|
|
696
1022
|
if (config.registry) {
|
|
697
1023
|
nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
|
|
@@ -700,13 +1026,57 @@ const module$1 = defineNuxtModule({
|
|
|
700
1026
|
config.registry
|
|
701
1027
|
);
|
|
702
1028
|
}
|
|
1029
|
+
if (config.defaultScriptOptions?.bundle !== void 0) {
|
|
1030
|
+
logger.warn(
|
|
1031
|
+
"`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
|
|
1035
|
+
const preset = process.env.NITRO_PRESET || "";
|
|
1036
|
+
const isStaticPreset = staticPresets.includes(preset);
|
|
1037
|
+
const firstPartyEnabled = !!config.firstParty;
|
|
1038
|
+
const firstPartyPrefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
|
|
1039
|
+
const firstPartyCollectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
|
|
1040
|
+
const firstPartyPrivacy = typeof config.firstParty === "object" ? config.firstParty.privacy ?? "anonymize" : "anonymize";
|
|
1041
|
+
const assetsPrefix = firstPartyPrefix || config.assets?.prefix || "/_scripts";
|
|
1042
|
+
if (config.partytown?.length) {
|
|
1043
|
+
config.registry = config.registry || {};
|
|
1044
|
+
const requiredForwards = [];
|
|
1045
|
+
for (const scriptKey of config.partytown) {
|
|
1046
|
+
const forwards = PARTYTOWN_FORWARDS[scriptKey];
|
|
1047
|
+
if (forwards) {
|
|
1048
|
+
requiredForwards.push(...forwards);
|
|
1049
|
+
} else if (import.meta.dev) {
|
|
1050
|
+
logger.warn(`[partytown] "${scriptKey}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
|
|
1051
|
+
}
|
|
1052
|
+
const reg = config.registry;
|
|
1053
|
+
const existing = reg[scriptKey];
|
|
1054
|
+
if (Array.isArray(existing)) {
|
|
1055
|
+
existing[1] = { ...existing[1], partytown: true };
|
|
1056
|
+
} else if (existing && typeof existing === "object" && existing !== true && existing !== "mock") {
|
|
1057
|
+
reg[scriptKey] = [existing, { partytown: true }];
|
|
1058
|
+
} else if (existing === true || existing === "mock") {
|
|
1059
|
+
reg[scriptKey] = [{}, { partytown: true }];
|
|
1060
|
+
} else {
|
|
1061
|
+
reg[scriptKey] = [{}, { partytown: true }];
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (requiredForwards.length && hasNuxtModule("@nuxtjs/partytown")) {
|
|
1065
|
+
const partytownConfig = nuxt.options.partytown || {};
|
|
1066
|
+
const existingForwards = partytownConfig.forward || [];
|
|
1067
|
+
const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...requiredForwards])];
|
|
1068
|
+
nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
|
|
1069
|
+
logger.info(`[partytown] Auto-configured forwards: ${requiredForwards.join(", ")}`);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
703
1072
|
const composables = [
|
|
704
1073
|
"useScript",
|
|
705
1074
|
"useScriptEventPage",
|
|
706
1075
|
"useScriptTriggerConsent",
|
|
707
1076
|
"useScriptTriggerElement",
|
|
708
1077
|
"useScriptTriggerIdleTimeout",
|
|
709
|
-
"useScriptTriggerInteraction"
|
|
1078
|
+
"useScriptTriggerInteraction",
|
|
1079
|
+
"useScriptTriggerServiceWorker"
|
|
710
1080
|
];
|
|
711
1081
|
for (const composable of composables) {
|
|
712
1082
|
addImports({
|
|
@@ -726,6 +1096,104 @@ const module$1 = defineNuxtModule({
|
|
|
726
1096
|
return templateTriggerResolver(config.defaultScriptOptions);
|
|
727
1097
|
}
|
|
728
1098
|
});
|
|
1099
|
+
const swHandlerPath = await resolvePath("./runtime/server/sw-handler");
|
|
1100
|
+
logger.debug("[nuxt-scripts] First-party config:", { firstPartyEnabled, firstPartyPrivacy, firstPartyCollectPrefix });
|
|
1101
|
+
if (firstPartyEnabled && !nuxt.options.dev) {
|
|
1102
|
+
const swPath = "/_nuxt-scripts-sw.js";
|
|
1103
|
+
const swRules = getSWInterceptRules(firstPartyCollectPrefix);
|
|
1104
|
+
addServerHandler({
|
|
1105
|
+
route: swPath,
|
|
1106
|
+
handler: swHandlerPath
|
|
1107
|
+
});
|
|
1108
|
+
addPluginTemplate({
|
|
1109
|
+
filename: "nuxt-scripts-sw-register.client.mjs",
|
|
1110
|
+
getContents() {
|
|
1111
|
+
return `import { defineNuxtPlugin } from 'nuxt/app'
|
|
1112
|
+
|
|
1113
|
+
export default defineNuxtPlugin({
|
|
1114
|
+
name: 'nuxt-scripts:sw-register',
|
|
1115
|
+
enforce: 'pre',
|
|
1116
|
+
async setup() {
|
|
1117
|
+
if (!('serviceWorker' in navigator)) return;
|
|
1118
|
+
|
|
1119
|
+
try {
|
|
1120
|
+
const reg = await navigator.serviceWorker.register('${swPath}', { scope: '/' });
|
|
1121
|
+
|
|
1122
|
+
// Wait for SW to be active and controlling this page
|
|
1123
|
+
if (!navigator.serviceWorker.controller) {
|
|
1124
|
+
await new Promise((resolve) => {
|
|
1125
|
+
const onControllerChange = () => {
|
|
1126
|
+
navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
|
|
1127
|
+
resolve();
|
|
1128
|
+
};
|
|
1129
|
+
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
1130
|
+
|
|
1131
|
+
// Fallback timeout
|
|
1132
|
+
setTimeout(resolve, 2000);
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
} catch (err) {
|
|
1136
|
+
console.warn('[nuxt-scripts] SW registration failed:', err);
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
})
|
|
1140
|
+
`;
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
addPluginTemplate({
|
|
1144
|
+
filename: "nuxt-scripts-beacon-intercept.client.mjs",
|
|
1145
|
+
getContents() {
|
|
1146
|
+
const rulesJson = JSON.stringify(swRules);
|
|
1147
|
+
return `export default defineNuxtPlugin({
|
|
1148
|
+
name: 'nuxt-scripts:beacon-intercept',
|
|
1149
|
+
enforce: 'pre',
|
|
1150
|
+
setup() {
|
|
1151
|
+
if (typeof navigator === 'undefined' || !navigator.sendBeacon) return;
|
|
1152
|
+
|
|
1153
|
+
const rules = ${rulesJson};
|
|
1154
|
+
const originalBeacon = navigator.sendBeacon.bind(navigator);
|
|
1155
|
+
|
|
1156
|
+
navigator.sendBeacon = (url, data) => {
|
|
1157
|
+
try {
|
|
1158
|
+
const parsed = new URL(url, window.location.origin);
|
|
1159
|
+
|
|
1160
|
+
// Check if this URL matches any of our proxy rules
|
|
1161
|
+
for (const rule of rules) {
|
|
1162
|
+
if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
|
|
1163
|
+
// Check path prefix if specified
|
|
1164
|
+
if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) {
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Rewrite to proxy: strip pathPrefix from original, prepend target
|
|
1169
|
+
const pathWithoutPrefix = rule.pathPrefix
|
|
1170
|
+
? parsed.pathname.slice(rule.pathPrefix.length)
|
|
1171
|
+
: parsed.pathname;
|
|
1172
|
+
const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
|
|
1173
|
+
const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;
|
|
1174
|
+
|
|
1175
|
+
return originalBeacon(proxyUrl, data);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
} catch (e) {
|
|
1179
|
+
// URL parsing failed, pass through
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
return originalBeacon(url, data);
|
|
1183
|
+
};
|
|
1184
|
+
},
|
|
1185
|
+
})
|
|
1186
|
+
`;
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
nuxt.options.runtimeConfig.public["nuxt-scripts-sw"] = { path: swPath };
|
|
1190
|
+
const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
|
|
1191
|
+
logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstPartyCollectPrefix}/**`, "->", proxyHandlerPath);
|
|
1192
|
+
addServerHandler({
|
|
1193
|
+
route: `${firstPartyCollectPrefix}/**`,
|
|
1194
|
+
handler: proxyHandlerPath
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
729
1197
|
const scripts = await registry(resolvePath);
|
|
730
1198
|
for (const script of scripts) {
|
|
731
1199
|
if (script.import?.name) {
|
|
@@ -743,42 +1211,7 @@ const module$1 = defineNuxtModule({
|
|
|
743
1211
|
}
|
|
744
1212
|
const registryScriptsWithImport = registryScripts.filter((i) => !!i.import?.name);
|
|
745
1213
|
const newScripts = registryScriptsWithImport.filter((i) => !scripts.some((r) => r.import?.name === i.import.name));
|
|
746
|
-
|
|
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
|
-
});
|
|
1214
|
+
registerTypeTemplates({ nuxt, config, newScripts });
|
|
782
1215
|
if (Object.keys(config.globals || {}).length || Object.keys(config.registry || {}).length) {
|
|
783
1216
|
addPluginTemplate({
|
|
784
1217
|
filename: `modules/${name.replace("/", "-")}/plugin.mjs`,
|
|
@@ -788,6 +1221,75 @@ export {}`;
|
|
|
788
1221
|
});
|
|
789
1222
|
}
|
|
790
1223
|
const { renderedScript } = setupPublicAssetStrategy(config.assets);
|
|
1224
|
+
if (firstPartyEnabled) {
|
|
1225
|
+
const proxyConfigs = getAllProxyConfigs(firstPartyCollectPrefix);
|
|
1226
|
+
const registryKeys = Object.keys(config.registry || {});
|
|
1227
|
+
const neededRoutes = {};
|
|
1228
|
+
const unsupportedScripts = [];
|
|
1229
|
+
for (const key of registryKeys) {
|
|
1230
|
+
const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
|
|
1231
|
+
const proxyKey = script?.proxy || void 0;
|
|
1232
|
+
if (proxyKey) {
|
|
1233
|
+
const proxyConfig = proxyConfigs[proxyKey];
|
|
1234
|
+
if (proxyConfig?.routes) {
|
|
1235
|
+
Object.assign(neededRoutes, proxyConfig.routes);
|
|
1236
|
+
} else {
|
|
1237
|
+
unsupportedScripts.push(key);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
if (unsupportedScripts.length && nuxt.options.dev) {
|
|
1242
|
+
logger.warn(
|
|
1243
|
+
`First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
|
|
1244
|
+
They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
const flatRoutes = {};
|
|
1248
|
+
for (const [path, config2] of Object.entries(neededRoutes)) {
|
|
1249
|
+
flatRoutes[path] = config2.proxy;
|
|
1250
|
+
}
|
|
1251
|
+
const allRewrites = [];
|
|
1252
|
+
for (const key of registryKeys) {
|
|
1253
|
+
const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
|
|
1254
|
+
const proxyKey = script?.proxy !== false ? script?.proxy || key : void 0;
|
|
1255
|
+
if (proxyKey) {
|
|
1256
|
+
const proxyConfig = proxyConfigs[proxyKey];
|
|
1257
|
+
if (proxyConfig?.rewrite) {
|
|
1258
|
+
allRewrites.push(...proxyConfig.rewrite);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
|
|
1263
|
+
routes: flatRoutes,
|
|
1264
|
+
privacy: firstPartyPrivacy,
|
|
1265
|
+
rewrites: allRewrites
|
|
1266
|
+
};
|
|
1267
|
+
if (Object.keys(neededRoutes).length) {
|
|
1268
|
+
if (nuxt.options.dev) {
|
|
1269
|
+
const routeCount = Object.keys(neededRoutes).length;
|
|
1270
|
+
const scriptsCount = registryKeys.length;
|
|
1271
|
+
logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${firstPartyPrivacy})`);
|
|
1272
|
+
if (logger.level >= 4) {
|
|
1273
|
+
for (const [path, config2] of Object.entries(neededRoutes)) {
|
|
1274
|
+
logger.debug(` ${path} \u2192 ${config2.proxy}`);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (isStaticPreset) {
|
|
1280
|
+
logger.warn(
|
|
1281
|
+
`First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
|
|
1282
|
+
Scripts will be bundled, but collection requests will not be proxied.
|
|
1283
|
+
|
|
1284
|
+
Options:
|
|
1285
|
+
1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
|
|
1286
|
+
2. Switch to server-rendered mode (ssr: true)
|
|
1287
|
+
3. Disable with firstParty: false
|
|
1288
|
+
|
|
1289
|
+
See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
791
1293
|
const moduleInstallPromises = /* @__PURE__ */ new Map();
|
|
792
1294
|
addBuildPlugin(NuxtScriptsCheckScripts(), {
|
|
793
1295
|
dev: true
|
|
@@ -795,15 +1297,18 @@ export {}`;
|
|
|
795
1297
|
addBuildPlugin(NuxtScriptBundleTransformer({
|
|
796
1298
|
scripts: registryScriptsWithImport,
|
|
797
1299
|
registryConfig: nuxt.options.runtimeConfig.public.scripts,
|
|
798
|
-
defaultBundle: config.defaultScriptOptions?.bundle,
|
|
1300
|
+
defaultBundle: firstPartyEnabled || config.defaultScriptOptions?.bundle,
|
|
1301
|
+
firstPartyEnabled,
|
|
1302
|
+
firstPartyCollectPrefix,
|
|
799
1303
|
moduleDetected(module) {
|
|
800
1304
|
if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
|
|
801
1305
|
moduleInstallPromises.set(module, () => installNuxtModule(module));
|
|
802
1306
|
},
|
|
803
|
-
assetsBaseURL:
|
|
1307
|
+
assetsBaseURL: assetsPrefix,
|
|
804
1308
|
fallbackOnSrcOnBundleFail: config.assets?.fallbackOnSrcOnBundleFail,
|
|
805
1309
|
fetchOptions: config.assets?.fetchOptions,
|
|
806
1310
|
cacheMaxAge: config.assets?.cacheMaxAge,
|
|
1311
|
+
integrity: config.assets?.integrity,
|
|
807
1312
|
renderedScript
|
|
808
1313
|
}));
|
|
809
1314
|
nuxt.hooks.hook("build:done", async () => {
|
|
@@ -812,8 +1317,35 @@ export {}`;
|
|
|
812
1317
|
await p?.();
|
|
813
1318
|
});
|
|
814
1319
|
});
|
|
815
|
-
if (
|
|
1320
|
+
if (config.googleStaticMapsProxy?.enabled) {
|
|
1321
|
+
addServerHandler({
|
|
1322
|
+
route: "/_scripts/google-static-maps-proxy",
|
|
1323
|
+
handler: await resolvePath("./runtime/server/google-static-maps-proxy")
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
addServerHandler({
|
|
1327
|
+
route: "/api/_scripts/x-embed",
|
|
1328
|
+
handler: await resolvePath("./runtime/server/x-embed")
|
|
1329
|
+
});
|
|
1330
|
+
addServerHandler({
|
|
1331
|
+
route: "/api/_scripts/x-embed-image",
|
|
1332
|
+
handler: await resolvePath("./runtime/server/x-embed-image")
|
|
1333
|
+
});
|
|
1334
|
+
addServerHandler({
|
|
1335
|
+
route: "/api/_scripts/instagram-embed",
|
|
1336
|
+
handler: await resolvePath("./runtime/server/instagram-embed")
|
|
1337
|
+
});
|
|
1338
|
+
addServerHandler({
|
|
1339
|
+
route: "/api/_scripts/instagram-embed-image",
|
|
1340
|
+
handler: await resolvePath("./runtime/server/instagram-embed-image")
|
|
1341
|
+
});
|
|
1342
|
+
addServerHandler({
|
|
1343
|
+
route: "/api/_scripts/instagram-embed-asset",
|
|
1344
|
+
handler: await resolvePath("./runtime/server/instagram-embed-asset")
|
|
1345
|
+
});
|
|
1346
|
+
if (nuxt.options.dev) {
|
|
816
1347
|
setupDevToolsUI(config, resolvePath);
|
|
1348
|
+
}
|
|
817
1349
|
}
|
|
818
1350
|
});
|
|
819
1351
|
|