@nuxt/scripts 1.0.0-rc.5 → 1.0.0-rc.7
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/bin/cli.mjs +2 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.mjs +50 -0
- package/dist/devtools-client/200.html +1 -1
- package/dist/devtools-client/404.html +1 -1
- package/dist/devtools-client/_nuxt/{CL6TeQIJ.js → 5D-5agUu.js} +31 -31
- package/dist/devtools-client/_nuxt/{DoF6byDH.js → BDlZgWHO.js} +1 -1
- package/dist/devtools-client/_nuxt/{D838xXrH.js → BntLcF3H.js} +1 -1
- package/dist/devtools-client/_nuxt/{BFtOB2Ap.js → CC9d18RE.js} +1 -1
- package/dist/devtools-client/_nuxt/{BfQVeAez.js → CaQ1scfO.js} +1 -1
- package/dist/devtools-client/_nuxt/{C7-YRs3P.js → DJ5bfe9v.js} +1 -1
- package/dist/devtools-client/_nuxt/{Duf9abe1.js → YKhzFESo.js} +1 -1
- package/dist/devtools-client/_nuxt/builds/latest.json +1 -1
- package/dist/devtools-client/_nuxt/builds/meta/7a96fd5e-d239-4ba5-816b-05034a861ba0.json +1 -0
- package/dist/devtools-client/_nuxt/error-404.Dwj0Wlzm.css +1 -0
- package/dist/devtools-client/_nuxt/error-500.B4wHUYBa.css +1 -0
- package/dist/devtools-client/docs/index.html +1 -1
- package/dist/devtools-client/first-party/index.html +1 -1
- package/dist/devtools-client/index.html +1 -1
- package/dist/devtools-client/registry/index.html +1 -1
- package/dist/module.d.mts +51 -2
- package/dist/module.d.ts +51 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +80 -9
- package/dist/registry.mjs +10 -10
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +73 -93
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +65 -57
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +73 -93
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.d.vue.ts +49 -28
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue +143 -103
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue.d.ts +49 -28
- package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.d.ts +50 -0
- package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.js +76 -1
- package/dist/runtime/server/bluesky-embed.js +3 -2
- package/dist/runtime/server/google-maps-geocode-proxy.js +3 -2
- package/dist/runtime/server/google-static-maps-proxy.js +3 -2
- package/dist/runtime/server/gravatar-proxy.js +3 -2
- package/dist/runtime/server/instagram-embed.js +3 -2
- package/dist/runtime/server/utils/image-proxy.js +3 -2
- package/dist/runtime/server/utils/sign.d.ts +109 -0
- package/dist/runtime/server/utils/sign.js +88 -0
- package/dist/runtime/server/utils/withSigning.d.ts +23 -0
- package/dist/runtime/server/utils/withSigning.js +18 -0
- package/dist/runtime/server/x-embed.js +3 -2
- package/dist/runtime/types.d.ts +9 -1
- package/dist/types-source.mjs +2 -198
- package/dist/types.d.mts +2 -2
- package/package.json +6 -2
- package/dist/devtools-client/_nuxt/builds/meta/03a9e297-6209-4281-8c3d-4265bdd5d038.json +0 -1
- package/dist/devtools-client/_nuxt/error-404.BGYgyi5Q.css +0 -1
- package/dist/devtools-client/_nuxt/error-500.7RpV0mcx.css +0 -1
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync, appendFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
2
3
|
import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, createResolver, extendViteConfig, logger as logger$1, useLogger, addTypeTemplate, defineNuxtModule, addPluginTemplate, addServerHandler, addImports, addComponentsDir, addTemplate, hasNuxtModule, addBuildPlugin } from '@nuxt/kit';
|
|
3
4
|
import { defu } from 'defu';
|
|
4
5
|
import { join, resolve } from 'pathe';
|
|
@@ -12,7 +13,6 @@ import { isCI, provider } from 'std-env';
|
|
|
12
13
|
import { parseAndWalk, ScopeTracker, walk, ScopeTrackerFunction, ScopeTrackerIdentifier, ScopeTrackerFunctionParam, ScopeTrackerVariable } from 'oxc-walker';
|
|
13
14
|
import { createUnplugin } from 'unplugin';
|
|
14
15
|
import { pathToFileURL } from 'node:url';
|
|
15
|
-
import { createHash } from 'node:crypto';
|
|
16
16
|
import fsp from 'node:fs/promises';
|
|
17
17
|
import { colors } from 'consola/utils';
|
|
18
18
|
import MagicString from 'magic-string';
|
|
@@ -1373,6 +1373,42 @@ function fixSelfClosingScriptComponents(nuxt) {
|
|
|
1373
1373
|
}
|
|
1374
1374
|
const UPPER_RE = /([A-Z])/g;
|
|
1375
1375
|
const toScreamingSnake = (s) => s.replace(UPPER_RE, "_$1").toUpperCase();
|
|
1376
|
+
const PROXY_SECRET_ENV_KEY = "NUXT_SCRIPTS_PROXY_SECRET";
|
|
1377
|
+
const PROXY_SECRET_ENV_LINE_RE = /^NUXT_SCRIPTS_PROXY_SECRET=/m;
|
|
1378
|
+
const PROXY_SECRET_ENV_VALUE_RE = /^NUXT_SCRIPTS_PROXY_SECRET=(.+)$/m;
|
|
1379
|
+
function resolveProxySecret(rootDir, isDev, configSecret, autoGenerate = true) {
|
|
1380
|
+
if (configSecret)
|
|
1381
|
+
return { secret: configSecret, ephemeral: false, source: "config" };
|
|
1382
|
+
const envSecret = process.env[PROXY_SECRET_ENV_KEY];
|
|
1383
|
+
if (envSecret)
|
|
1384
|
+
return { secret: envSecret, ephemeral: false, source: "env" };
|
|
1385
|
+
if (!isDev || !autoGenerate)
|
|
1386
|
+
return void 0;
|
|
1387
|
+
const secret = randomBytes(32).toString("hex");
|
|
1388
|
+
const envPath = resolve(rootDir, ".env");
|
|
1389
|
+
const line = `${PROXY_SECRET_ENV_KEY}=${secret}
|
|
1390
|
+
`;
|
|
1391
|
+
try {
|
|
1392
|
+
if (existsSync(envPath)) {
|
|
1393
|
+
const contents = readFileSync(envPath, "utf-8");
|
|
1394
|
+
if (PROXY_SECRET_ENV_LINE_RE.test(contents)) {
|
|
1395
|
+
const match = contents.match(PROXY_SECRET_ENV_VALUE_RE);
|
|
1396
|
+
if (match?.[1])
|
|
1397
|
+
return { secret: match[1].trim(), ephemeral: false, source: "dotenv-generated" };
|
|
1398
|
+
}
|
|
1399
|
+
appendFileSync(envPath, contents.endsWith("\n") ? line : `
|
|
1400
|
+
${line}`);
|
|
1401
|
+
} else {
|
|
1402
|
+
writeFileSync(envPath, `# Generated by @nuxt/scripts
|
|
1403
|
+
${line}`);
|
|
1404
|
+
}
|
|
1405
|
+
process.env[PROXY_SECRET_ENV_KEY] = secret;
|
|
1406
|
+
return { secret, ephemeral: false, source: "dotenv-generated" };
|
|
1407
|
+
} catch {
|
|
1408
|
+
process.env[PROXY_SECRET_ENV_KEY] = secret;
|
|
1409
|
+
return { secret, ephemeral: true, source: "memory-generated" };
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1376
1412
|
function isProxyDisabled(registryKey, registry2, runtimeConfig) {
|
|
1377
1413
|
const entry = registry2?.[registryKey];
|
|
1378
1414
|
if (!entry)
|
|
@@ -1684,12 +1720,12 @@ They will load directly from third-party servers.`
|
|
|
1684
1720
|
if (totalDomains > 0 && nuxt.options.dev) {
|
|
1685
1721
|
logger.success(`Proxy mode enabled for ${registryKeys.length} script(s), ${totalDomains} domain(s) proxied (privacy: ${privacyLabel})`);
|
|
1686
1722
|
}
|
|
1687
|
-
const
|
|
1688
|
-
const
|
|
1689
|
-
if (
|
|
1723
|
+
const proxyStaticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
|
|
1724
|
+
const proxyPreset = process.env.NITRO_PRESET || "";
|
|
1725
|
+
if (proxyStaticPresets.includes(proxyPreset)) {
|
|
1690
1726
|
logger.warn(
|
|
1691
|
-
`Proxy collection endpoints require a server runtime (detected: ${
|
|
1692
|
-
Scripts will be bundled, but collection requests will not be proxied.
|
|
1727
|
+
`Proxy collection endpoints require a server runtime (detected: ${proxyPreset || "static"}).
|
|
1728
|
+
Scripts will be bundled, but collection requests will not be proxied and URL signing will be unavailable.
|
|
1693
1729
|
Options: configure platform rewrites, switch to server-rendered mode, or disable with proxy: false.`
|
|
1694
1730
|
);
|
|
1695
1731
|
}
|
|
@@ -1736,6 +1772,7 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
|
|
|
1736
1772
|
});
|
|
1737
1773
|
const scriptsPrefix = config.prefix || "/_scripts";
|
|
1738
1774
|
const enabledEndpoints = {};
|
|
1775
|
+
let anyHandlerRequiresSigning = false;
|
|
1739
1776
|
for (const script of scripts) {
|
|
1740
1777
|
if (!script.serverHandlers?.length || !script.registryKey)
|
|
1741
1778
|
continue;
|
|
@@ -1744,11 +1781,14 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
|
|
|
1744
1781
|
continue;
|
|
1745
1782
|
enabledEndpoints[script.registryKey] = true;
|
|
1746
1783
|
for (const handler of script.serverHandlers) {
|
|
1784
|
+
const resolvedRoute = handler.route.replace("/_scripts", scriptsPrefix);
|
|
1747
1785
|
addServerHandler({
|
|
1748
|
-
route:
|
|
1786
|
+
route: resolvedRoute,
|
|
1749
1787
|
handler: handler.handler,
|
|
1750
1788
|
middleware: handler.middleware
|
|
1751
1789
|
});
|
|
1790
|
+
if (handler.requiresSigning)
|
|
1791
|
+
anyHandlerRequiresSigning = true;
|
|
1752
1792
|
}
|
|
1753
1793
|
if (script.registryKey === "gravatar") {
|
|
1754
1794
|
const gravatarConfig = config.registry?.gravatar?.[0] || {};
|
|
@@ -1768,7 +1808,38 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
|
|
|
1768
1808
|
{ endpoints: enabledEndpoints },
|
|
1769
1809
|
nuxt.options.runtimeConfig.public["nuxt-scripts"]
|
|
1770
1810
|
);
|
|
1811
|
+
const staticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
|
|
1812
|
+
const nitroPreset = process.env.NITRO_PRESET || "";
|
|
1813
|
+
const isStaticTarget = staticPresets.includes(nitroPreset);
|
|
1814
|
+
const isSpa = nuxt.options.ssr === false;
|
|
1815
|
+
if (anyHandlerRequiresSigning && (isSpa || isStaticTarget)) {
|
|
1816
|
+
logger.warn(
|
|
1817
|
+
`[security] URL signing requires a server runtime${isStaticTarget ? ` (detected preset: ${nitroPreset})` : " (ssr: false)"}.
|
|
1818
|
+
Proxy endpoints will work without signature verification.
|
|
1819
|
+
To enable signing, deploy with a server-rendered target or configure platform-level rewrites.`
|
|
1820
|
+
);
|
|
1821
|
+
} else if (anyHandlerRequiresSigning) {
|
|
1822
|
+
const proxySecretResolved = resolveProxySecret(
|
|
1823
|
+
nuxt.options.rootDir,
|
|
1824
|
+
!!nuxt.options.dev,
|
|
1825
|
+
config.security?.secret,
|
|
1826
|
+
config.security?.autoGenerateSecret !== false
|
|
1827
|
+
);
|
|
1828
|
+
if (proxySecretResolved?.source === "dotenv-generated")
|
|
1829
|
+
logger.info(`[security] Generated ${PROXY_SECRET_ENV_KEY} in .env for signed proxy URLs.`);
|
|
1830
|
+
else if (proxySecretResolved?.source === "memory-generated")
|
|
1831
|
+
logger.warn(`[security] Generated an in-memory ${PROXY_SECRET_ENV_KEY} (could not write .env). Signed URLs will break across restarts.`);
|
|
1832
|
+
if (proxySecretResolved?.secret) {
|
|
1833
|
+
nuxt.options.runtimeConfig["nuxt-scripts"].proxySecret = proxySecretResolved.secret;
|
|
1834
|
+
} else if (!nuxt.options.dev) {
|
|
1835
|
+
logger.warn(
|
|
1836
|
+
`[security] ${PROXY_SECRET_ENV_KEY} is not set. Proxy endpoints will pass requests through without signature verification.
|
|
1837
|
+
Generate one with: npx @nuxt/scripts generate-secret
|
|
1838
|
+
Then set the env var: ${PROXY_SECRET_ENV_KEY}=<secret>`
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1771
1842
|
}
|
|
1772
1843
|
});
|
|
1773
1844
|
|
|
1774
|
-
export { applyAutoInject, module$1 as default, isProxyDisabled };
|
|
1845
|
+
export { applyAutoInject, module$1 as default, isProxyDisabled, resolveProxySecret };
|
package/dist/registry.mjs
CHANGED
|
@@ -554,8 +554,8 @@ async function registry(resolve) {
|
|
|
554
554
|
envDefaults: { apiKey: "" },
|
|
555
555
|
category: "content",
|
|
556
556
|
serverHandlers: [
|
|
557
|
-
{ route: "/_scripts/proxy/google-static-maps", handler: "./runtime/server/google-static-maps-proxy" },
|
|
558
|
-
{ route: "/_scripts/proxy/google-maps-geocode", handler: "./runtime/server/google-maps-geocode-proxy" }
|
|
557
|
+
{ route: "/_scripts/proxy/google-static-maps", handler: "./runtime/server/google-static-maps-proxy", requiresSigning: true },
|
|
558
|
+
{ route: "/_scripts/proxy/google-maps-geocode", handler: "./runtime/server/google-maps-geocode-proxy", requiresSigning: true }
|
|
559
559
|
]
|
|
560
560
|
}),
|
|
561
561
|
def("blueskyEmbed", {
|
|
@@ -564,8 +564,8 @@ async function registry(resolve) {
|
|
|
564
564
|
label: "Bluesky Embed",
|
|
565
565
|
category: "content",
|
|
566
566
|
serverHandlers: [
|
|
567
|
-
{ route: "/_scripts/embed/bluesky", handler: "./runtime/server/bluesky-embed" },
|
|
568
|
-
{ route: "/_scripts/embed/bluesky-image", handler: "./runtime/server/bluesky-embed-image" }
|
|
567
|
+
{ route: "/_scripts/embed/bluesky", handler: "./runtime/server/bluesky-embed", requiresSigning: true },
|
|
568
|
+
{ route: "/_scripts/embed/bluesky-image", handler: "./runtime/server/bluesky-embed-image", requiresSigning: true }
|
|
569
569
|
]
|
|
570
570
|
}),
|
|
571
571
|
def("instagramEmbed", {
|
|
@@ -574,9 +574,9 @@ async function registry(resolve) {
|
|
|
574
574
|
label: "Instagram Embed",
|
|
575
575
|
category: "content",
|
|
576
576
|
serverHandlers: [
|
|
577
|
-
{ route: "/_scripts/embed/instagram", handler: "./runtime/server/instagram-embed" },
|
|
578
|
-
{ route: "/_scripts/embed/instagram-image", handler: "./runtime/server/instagram-embed-image" },
|
|
579
|
-
{ route: "/_scripts/embed/instagram-asset", handler: "./runtime/server/instagram-embed-asset" }
|
|
577
|
+
{ route: "/_scripts/embed/instagram", handler: "./runtime/server/instagram-embed", requiresSigning: true },
|
|
578
|
+
{ route: "/_scripts/embed/instagram-image", handler: "./runtime/server/instagram-embed-image", requiresSigning: true },
|
|
579
|
+
{ route: "/_scripts/embed/instagram-asset", handler: "./runtime/server/instagram-embed-asset", requiresSigning: true }
|
|
580
580
|
]
|
|
581
581
|
}),
|
|
582
582
|
def("xEmbed", {
|
|
@@ -585,8 +585,8 @@ async function registry(resolve) {
|
|
|
585
585
|
label: "X Embed",
|
|
586
586
|
category: "content",
|
|
587
587
|
serverHandlers: [
|
|
588
|
-
{ route: "/_scripts/embed/x", handler: "./runtime/server/x-embed" },
|
|
589
|
-
{ route: "/_scripts/embed/x-image", handler: "./runtime/server/x-embed-image" }
|
|
588
|
+
{ route: "/_scripts/embed/x", handler: "./runtime/server/x-embed", requiresSigning: true },
|
|
589
|
+
{ route: "/_scripts/embed/x-image", handler: "./runtime/server/x-embed-image", requiresSigning: true }
|
|
590
590
|
]
|
|
591
591
|
}),
|
|
592
592
|
// support
|
|
@@ -690,7 +690,7 @@ async function registry(resolve) {
|
|
|
690
690
|
privacy: PRIVACY_IP_ONLY
|
|
691
691
|
},
|
|
692
692
|
serverHandlers: [
|
|
693
|
-
{ route: "/_scripts/proxy/gravatar", handler: "./runtime/server/gravatar-proxy" }
|
|
693
|
+
{ route: "/_scripts/proxy/gravatar", handler: "./runtime/server/gravatar-proxy", requiresSigning: true }
|
|
694
694
|
]
|
|
695
695
|
})
|
|
696
696
|
]);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { ElementScriptTrigger } from '#nuxt-scripts/types';
|
|
2
2
|
import type { HTMLAttributes, ReservedProps, ShallowRef } from 'vue';
|
|
3
3
|
export { MAP_INJECTION_KEY } from './useGoogleMapsResource.js';
|
|
4
|
-
|
|
5
|
-
export default _default;
|
|
6
|
-
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
4
|
+
export interface ScriptGoogleMapsProps {
|
|
7
5
|
/**
|
|
8
6
|
* Defines the trigger event to load the script.
|
|
7
|
+
* @default ['mouseenter', 'mouseover', 'mousedown']
|
|
9
8
|
*/
|
|
10
9
|
trigger?: ElementScriptTrigger;
|
|
11
10
|
/**
|
|
@@ -14,11 +13,20 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
14
13
|
apiKey?: string;
|
|
15
14
|
/**
|
|
16
15
|
* A latitude / longitude of where to focus the map.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated Pass `center` via `mapOptions` instead. The top-level `center`
|
|
18
|
+
* prop will be removed in a future major version. When both are set,
|
|
19
|
+
* `mapOptions.center` wins.
|
|
20
|
+
* @see https://scripts.nuxt.com/docs/migration-guide/v0-to-v1
|
|
17
21
|
*/
|
|
18
22
|
center?: google.maps.LatLng | google.maps.LatLngLiteral | `${string},${string}`;
|
|
19
23
|
/**
|
|
20
24
|
* Zoom level for the map (0-21). Reactive: changing this will update the map.
|
|
21
|
-
*
|
|
25
|
+
*
|
|
26
|
+
* @deprecated Pass `zoom` via `mapOptions` instead. The top-level `zoom`
|
|
27
|
+
* prop will be removed in a future major version. When both are set,
|
|
28
|
+
* `mapOptions.zoom` wins.
|
|
29
|
+
* @see https://scripts.nuxt.com/docs/migration-guide/v0-to-v1
|
|
22
30
|
*/
|
|
23
31
|
zoom?: number;
|
|
24
32
|
/**
|
|
@@ -30,19 +38,21 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
30
38
|
*/
|
|
31
39
|
region?: string;
|
|
32
40
|
/**
|
|
33
|
-
* Defines the language of the map
|
|
41
|
+
* Defines the language of the map.
|
|
34
42
|
*/
|
|
35
43
|
language?: string;
|
|
36
44
|
/**
|
|
37
|
-
* Defines the version of google maps js API
|
|
45
|
+
* Defines the version of google maps js API.
|
|
38
46
|
*/
|
|
39
47
|
version?: string;
|
|
40
48
|
/**
|
|
41
49
|
* Defines the width of the map.
|
|
50
|
+
* @default 640
|
|
42
51
|
*/
|
|
43
52
|
width?: number | string;
|
|
44
53
|
/**
|
|
45
|
-
* Defines the height of the map
|
|
54
|
+
* Defines the height of the map.
|
|
55
|
+
* @default 400
|
|
46
56
|
*/
|
|
47
57
|
height?: number | string;
|
|
48
58
|
/**
|
|
@@ -60,122 +70,92 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
60
70
|
};
|
|
61
71
|
/**
|
|
62
72
|
* Manual color mode control. When provided, overrides auto-detection from @nuxtjs/color-mode.
|
|
63
|
-
* Accepts 'light'
|
|
64
|
-
*/
|
|
65
|
-
colorMode?: "light" | "dark";
|
|
66
|
-
}, {
|
|
67
|
-
readonly googleMaps: import("vue").Ref<typeof google.maps | undefined, typeof google.maps | undefined>;
|
|
68
|
-
readonly map: ShallowRef<google.maps.Map | undefined>;
|
|
69
|
-
readonly resolveQueryToLatLng: (query: string) => Promise<google.maps.LatLng | google.maps.LatLngLiteral | undefined>;
|
|
70
|
-
readonly importLibrary: {
|
|
71
|
-
(key: "marker"): Promise<google.maps.MarkerLibrary>;
|
|
72
|
-
(key: "places"): Promise<google.maps.PlacesLibrary>;
|
|
73
|
-
(key: "geometry"): Promise<google.maps.GeometryLibrary>;
|
|
74
|
-
(key: "drawing"): Promise<google.maps.DrawingLibrary>;
|
|
75
|
-
(key: "visualization"): Promise<google.maps.VisualizationLibrary>;
|
|
76
|
-
(key: string): Promise<any>;
|
|
77
|
-
};
|
|
78
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
79
|
-
error: () => any;
|
|
80
|
-
ready: (e: {
|
|
81
|
-
readonly googleMaps: import("vue").Ref<typeof google.maps | undefined, typeof google.maps | undefined>;
|
|
82
|
-
readonly map: ShallowRef<google.maps.Map | undefined>;
|
|
83
|
-
readonly resolveQueryToLatLng: (query: string) => Promise<google.maps.LatLng | google.maps.LatLngLiteral | undefined>;
|
|
84
|
-
readonly importLibrary: {
|
|
85
|
-
(key: "marker"): Promise<google.maps.MarkerLibrary>;
|
|
86
|
-
(key: "places"): Promise<google.maps.PlacesLibrary>;
|
|
87
|
-
(key: "geometry"): Promise<google.maps.GeometryLibrary>;
|
|
88
|
-
(key: "drawing"): Promise<google.maps.DrawingLibrary>;
|
|
89
|
-
(key: "visualization"): Promise<google.maps.VisualizationLibrary>;
|
|
90
|
-
(key: string): Promise<any>;
|
|
91
|
-
};
|
|
92
|
-
}) => any;
|
|
93
|
-
}, string, import("vue").PublicProps, Readonly<{
|
|
94
|
-
/**
|
|
95
|
-
* Defines the trigger event to load the script.
|
|
73
|
+
* Accepts 'light' or 'dark'.
|
|
96
74
|
*/
|
|
97
|
-
|
|
75
|
+
colorMode?: 'light' | 'dark';
|
|
76
|
+
}
|
|
77
|
+
export interface ScriptGoogleMapsExpose {
|
|
98
78
|
/**
|
|
99
|
-
*
|
|
79
|
+
* A reference to the loaded Google Maps API namespace (`google.maps`), or
|
|
80
|
+
* `undefined` if not yet loaded.
|
|
100
81
|
*/
|
|
101
|
-
|
|
82
|
+
mapsApi: ShallowRef<typeof google.maps | undefined>;
|
|
102
83
|
/**
|
|
103
|
-
* A
|
|
84
|
+
* A reference to the loaded Google Maps API namespace, or `undefined` if not
|
|
85
|
+
* yet loaded.
|
|
86
|
+
*
|
|
87
|
+
* @deprecated Use `mapsApi` instead. The `googleMaps` alias will be removed
|
|
88
|
+
* in a future major version.
|
|
89
|
+
* @see https://scripts.nuxt.com/docs/migration-guide/v0-to-v1
|
|
104
90
|
*/
|
|
105
|
-
|
|
91
|
+
googleMaps: ShallowRef<typeof google.maps | undefined>;
|
|
106
92
|
/**
|
|
107
|
-
*
|
|
108
|
-
* Takes precedence over mapOptions.zoom when provided.
|
|
93
|
+
* A reference to the Google Map instance, or `undefined` if not yet initialized.
|
|
109
94
|
*/
|
|
110
|
-
|
|
95
|
+
map: ShallowRef<google.maps.Map | undefined>;
|
|
111
96
|
/**
|
|
112
|
-
*
|
|
97
|
+
* Utility function to resolve a location query (e.g. "New York, NY") to latitude/longitude coordinates.
|
|
98
|
+
* Uses a caching mechanism and a server-side proxy to avoid unnecessary client-side API calls.
|
|
113
99
|
*/
|
|
114
|
-
|
|
100
|
+
resolveQueryToLatLng: (query: string) => Promise<google.maps.LatLng | google.maps.LatLngLiteral | undefined>;
|
|
115
101
|
/**
|
|
116
|
-
*
|
|
102
|
+
* Utility function to dynamically import additional Google Maps libraries (e.g. "marker", "places").
|
|
103
|
+
* Caches imported libraries for efficient reuse.
|
|
117
104
|
*/
|
|
118
|
-
|
|
105
|
+
importLibrary: {
|
|
106
|
+
(key: 'marker'): Promise<google.maps.MarkerLibrary>;
|
|
107
|
+
(key: 'places'): Promise<google.maps.PlacesLibrary>;
|
|
108
|
+
(key: 'geometry'): Promise<google.maps.GeometryLibrary>;
|
|
109
|
+
(key: 'drawing'): Promise<google.maps.DrawingLibrary>;
|
|
110
|
+
(key: 'visualization'): Promise<google.maps.VisualizationLibrary>;
|
|
111
|
+
(key: string): Promise<any>;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export interface ScriptGoogleMapsEmits {
|
|
119
115
|
/**
|
|
120
|
-
*
|
|
116
|
+
* Fired when the Google Maps instance is fully loaded and ready to use. Provides access to the maps API.
|
|
121
117
|
*/
|
|
122
|
-
|
|
118
|
+
ready: [payload: ScriptGoogleMapsExpose];
|
|
123
119
|
/**
|
|
124
|
-
*
|
|
120
|
+
* Fired when the Google Maps script fails to load.
|
|
125
121
|
*/
|
|
126
|
-
|
|
122
|
+
error: [];
|
|
123
|
+
}
|
|
124
|
+
export interface ScriptGoogleMapsSlots {
|
|
127
125
|
/**
|
|
128
|
-
*
|
|
126
|
+
* Default slot for rendering child components (e.g. markers, info windows) that depend on the map being ready.
|
|
129
127
|
*/
|
|
130
|
-
|
|
128
|
+
default?: () => any;
|
|
131
129
|
/**
|
|
132
|
-
*
|
|
130
|
+
* Slot displayed while the map is loading. Can be used to show a custom loading indicator.
|
|
133
131
|
*/
|
|
134
|
-
|
|
132
|
+
loading?: () => any;
|
|
135
133
|
/**
|
|
136
|
-
*
|
|
134
|
+
* Slot displayed when the script is awaiting user interaction to load (based on the `trigger` prop).
|
|
137
135
|
*/
|
|
138
|
-
|
|
136
|
+
awaitingLoad?: () => any;
|
|
139
137
|
/**
|
|
140
|
-
*
|
|
141
|
-
* When provided, the map will automatically switch styles based on color mode.
|
|
142
|
-
* Requires @nuxtjs/color-mode or manual colorMode prop.
|
|
138
|
+
* Slot displayed if the script fails to load.
|
|
143
139
|
*/
|
|
144
|
-
|
|
145
|
-
light?: string;
|
|
146
|
-
dark?: string;
|
|
147
|
-
};
|
|
140
|
+
error?: () => any;
|
|
148
141
|
/**
|
|
149
|
-
*
|
|
150
|
-
* Accepts 'light', 'dark', or a reactive ref.
|
|
142
|
+
* Slot displayed as a placeholder before the map is ready. Useful for showing a static map or skeleton.
|
|
151
143
|
*/
|
|
152
|
-
|
|
153
|
-
}
|
|
144
|
+
placeholder?: () => any;
|
|
145
|
+
}
|
|
146
|
+
declare const _default: typeof __VLS_export;
|
|
147
|
+
export default _default;
|
|
148
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<ScriptGoogleMapsProps, ScriptGoogleMapsExpose, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
149
|
+
error: () => any;
|
|
150
|
+
ready: (payload: ScriptGoogleMapsExpose) => any;
|
|
151
|
+
}, string, import("vue").PublicProps, Readonly<ScriptGoogleMapsProps> & Readonly<{
|
|
154
152
|
onError?: (() => any) | undefined;
|
|
155
|
-
onReady?: ((
|
|
156
|
-
readonly googleMaps: import("vue").Ref<typeof google.maps | undefined, typeof google.maps | undefined>;
|
|
157
|
-
readonly map: ShallowRef<google.maps.Map | undefined>;
|
|
158
|
-
readonly resolveQueryToLatLng: (query: string) => Promise<google.maps.LatLng | google.maps.LatLngLiteral | undefined>;
|
|
159
|
-
readonly importLibrary: {
|
|
160
|
-
(key: "marker"): Promise<google.maps.MarkerLibrary>;
|
|
161
|
-
(key: "places"): Promise<google.maps.PlacesLibrary>;
|
|
162
|
-
(key: "geometry"): Promise<google.maps.GeometryLibrary>;
|
|
163
|
-
(key: "drawing"): Promise<google.maps.DrawingLibrary>;
|
|
164
|
-
(key: "visualization"): Promise<google.maps.VisualizationLibrary>;
|
|
165
|
-
(key: string): Promise<any>;
|
|
166
|
-
};
|
|
167
|
-
}) => any) | undefined;
|
|
153
|
+
onReady?: ((payload: ScriptGoogleMapsExpose) => any) | undefined;
|
|
168
154
|
}>, {
|
|
169
155
|
trigger: ElementScriptTrigger;
|
|
170
156
|
width: number | string;
|
|
171
157
|
height: number | string;
|
|
172
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>,
|
|
173
|
-
default?: () => any;
|
|
174
|
-
placeholder?: () => any;
|
|
175
|
-
loading?: () => any;
|
|
176
|
-
awaitingLoad?: () => any;
|
|
177
|
-
error?: () => any;
|
|
178
|
-
}>;
|
|
158
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, ScriptGoogleMapsSlots>;
|
|
179
159
|
type __VLS_WithSlots<T, S> = T & {
|
|
180
160
|
new (): {
|
|
181
161
|
$slots: S;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
export { MAP_INJECTION_KEY } from "./useGoogleMapsResource";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
2
6
|
import { useScriptTriggerElement } from "#nuxt-scripts/composables/useScriptTriggerElement";
|
|
3
7
|
import { useScriptGoogleMaps } from "#nuxt-scripts/registry/google-maps";
|
|
4
8
|
import { scriptRuntimeConfig, scriptsPrefix } from "#nuxt-scripts/utils";
|
|
5
9
|
import { defu } from "defu";
|
|
6
10
|
import { tryUseNuxtApp, useHead, useRuntimeConfig } from "nuxt/app";
|
|
7
|
-
import { computed, onBeforeUnmount, onMounted, provide, ref, shallowRef, toRaw, useAttrs, watch } from "vue";
|
|
11
|
+
import { computed, onBeforeUnmount, onMounted, provide, ref, shallowRef, toRaw, useAttrs, useTemplateRef, watch } from "vue";
|
|
8
12
|
import ScriptAriaLoadingIndicator from "../ScriptAriaLoadingIndicator.vue";
|
|
9
|
-
import { MAP_INJECTION_KEY } from "./useGoogleMapsResource";
|
|
10
|
-
const DIGITS_ONLY_RE = /^\d+$/;
|
|
11
|
-
const DIGITS_PX_RE = /^\d+px$/i;
|
|
12
|
-
export { MAP_INJECTION_KEY } from "./useGoogleMapsResource";
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<script setup>
|
|
13
|
+
import { defineDeprecatedAlias, MAP_INJECTION_KEY, waitForMapsReady, warnDeprecatedTopLevelMapProps } from "./useGoogleMapsResource";
|
|
16
14
|
const props = defineProps({
|
|
17
15
|
trigger: { type: [String, Array, Boolean], required: false, default: ["mouseenter", "mouseover", "mousedown"] },
|
|
18
16
|
apiKey: { type: String, required: false },
|
|
@@ -30,23 +28,21 @@ const props = defineProps({
|
|
|
30
28
|
});
|
|
31
29
|
const emits = defineEmits(["ready", "error"]);
|
|
32
30
|
defineSlots();
|
|
31
|
+
const DIGITS_ONLY_RE = /^\d+$/;
|
|
32
|
+
const DIGITS_PX_RE = /^\d+px$/i;
|
|
33
33
|
const apiKey = props.apiKey || scriptRuntimeConfig("googleMaps")?.apiKey;
|
|
34
34
|
const runtimeConfig = useRuntimeConfig();
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
if (props.colorMode)
|
|
39
|
-
return props.colorMode;
|
|
40
|
-
if (nuxtColorMode?.value)
|
|
41
|
-
return nuxtColorMode.value === "dark" ? "dark" : "light";
|
|
42
|
-
return "light";
|
|
35
|
+
const nuxtColorMode = computed(() => {
|
|
36
|
+
const value = tryUseNuxtApp()?.$colorMode?.value;
|
|
37
|
+
return value === "dark" || value === "light" ? value : void 0;
|
|
43
38
|
});
|
|
39
|
+
const currentColorMode = computed(() => props.colorMode || nuxtColorMode.value || "light");
|
|
44
40
|
const currentMapId = computed(() => {
|
|
45
41
|
if (!props.mapIds)
|
|
46
42
|
return props.mapOptions?.mapId;
|
|
47
43
|
return props.mapIds[currentColorMode.value] || props.mapIds.light || props.mapOptions?.mapId;
|
|
48
44
|
});
|
|
49
|
-
const mapsApi =
|
|
45
|
+
const mapsApi = shallowRef();
|
|
50
46
|
if (import.meta.dev) {
|
|
51
47
|
if (!apiKey)
|
|
52
48
|
throw new Error("GoogleMaps requires an API key. Enable it in your nuxt.config:\n\n scripts: {\n registry: {\n googleMaps: true\n }\n }\n\nThen set NUXT_PUBLIC_SCRIPTS_GOOGLE_MAPS_API_KEY in your .env file.\n\nAlternatively, pass `api-key` directly on the <ScriptGoogleMaps> component (note: this exposes the key client-side).");
|
|
@@ -62,9 +58,10 @@ if (import.meta.dev) {
|
|
|
62
58
|
if (prop in attrs)
|
|
63
59
|
console.warn(`[nuxt-scripts] <ScriptGoogleMaps> prop "${prop}" was removed in v1. ${message} See https://scripts.nuxt.com/docs/migration-guide/v0-to-v1`);
|
|
64
60
|
}
|
|
61
|
+
warnDeprecatedTopLevelMapProps({ center: props.center, zoom: props.zoom });
|
|
65
62
|
}
|
|
66
|
-
const rootEl =
|
|
67
|
-
const mapEl =
|
|
63
|
+
const rootEl = useTemplateRef("rootEl");
|
|
64
|
+
const mapEl = useTemplateRef("mapEl");
|
|
68
65
|
const centerOverride = ref();
|
|
69
66
|
const trigger = useScriptTriggerElement({ trigger: props.trigger, el: rootEl });
|
|
70
67
|
const { load, status, onLoaded } = useScriptGoogleMaps({
|
|
@@ -78,12 +75,14 @@ const { load, status, onLoaded } = useScriptGoogleMaps({
|
|
|
78
75
|
});
|
|
79
76
|
const options = computed(() => {
|
|
80
77
|
const mapId = props.mapOptions?.styles ? void 0 : currentMapId.value || "map";
|
|
81
|
-
return defu(
|
|
82
|
-
center:
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
return defu(
|
|
79
|
+
{ center: centerOverride.value, mapId },
|
|
80
|
+
props.mapOptions,
|
|
81
|
+
{ center: props.center, zoom: props.zoom },
|
|
82
|
+
{ zoom: 15 }
|
|
83
|
+
);
|
|
85
84
|
});
|
|
86
|
-
const
|
|
85
|
+
const isMapReady = ref(false);
|
|
87
86
|
const map = shallowRef();
|
|
88
87
|
function isLocationQuery(s) {
|
|
89
88
|
return typeof s === "string" && (s.split(",").length > 2 || s.includes("+"));
|
|
@@ -108,29 +107,25 @@ async function resolveQueryToLatLng(query) {
|
|
|
108
107
|
}
|
|
109
108
|
throw new Error(`No location found for ${query}`);
|
|
110
109
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return resolve(results[0].geometry.location);
|
|
128
|
-
return reject(new Error(`No location found for ${query}`));
|
|
129
|
-
});
|
|
130
|
-
}).then((res) => {
|
|
131
|
-
queryToLatLngCache.set(query, res);
|
|
132
|
-
return res;
|
|
110
|
+
await waitForMapsReady({ mapsApi, map, status, load });
|
|
111
|
+
const placesService = new mapsApi.value.places.PlacesService(map.value);
|
|
112
|
+
const result = await new Promise((resolve, reject) => {
|
|
113
|
+
placesService.findPlaceFromQuery(
|
|
114
|
+
{
|
|
115
|
+
query,
|
|
116
|
+
fields: ["name", "geometry"]
|
|
117
|
+
},
|
|
118
|
+
(results, status2) => {
|
|
119
|
+
if (status2 === "OK" && results?.[0]?.geometry?.location) {
|
|
120
|
+
resolve(results[0].geometry.location);
|
|
121
|
+
} else {
|
|
122
|
+
reject(new Error(`No location found for ${query}`));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
133
126
|
});
|
|
127
|
+
queryToLatLngCache.set(query, result);
|
|
128
|
+
return result;
|
|
134
129
|
}
|
|
135
130
|
const libraries = /* @__PURE__ */ new Map();
|
|
136
131
|
function importLibrary(key) {
|
|
@@ -152,13 +147,25 @@ function importLibrary(key) {
|
|
|
152
147
|
libraries.set(key, cached);
|
|
153
148
|
return cached;
|
|
154
149
|
}
|
|
155
|
-
const
|
|
150
|
+
const exposed = {
|
|
151
|
+
mapsApi,
|
|
152
|
+
// Plain alias for production. In dev, replaced below with a getter that
|
|
153
|
+
// emits a one-shot deprecation warning. Both forms return the same
|
|
154
|
+
// shallow ref as `mapsApi`.
|
|
156
155
|
googleMaps: mapsApi,
|
|
157
156
|
map,
|
|
158
157
|
resolveQueryToLatLng,
|
|
159
158
|
importLibrary
|
|
160
159
|
};
|
|
161
|
-
|
|
160
|
+
if (import.meta.dev) {
|
|
161
|
+
defineDeprecatedAlias(
|
|
162
|
+
exposed,
|
|
163
|
+
"googleMaps",
|
|
164
|
+
"mapsApi",
|
|
165
|
+
'[nuxt-scripts] <ScriptGoogleMaps> expose key "googleMaps" is deprecated; use "mapsApi" instead. See https://scripts.nuxt.com/docs/migration-guide/v0-to-v1'
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
defineExpose(exposed);
|
|
162
169
|
let activeInfoWindow;
|
|
163
170
|
provide(MAP_INJECTION_KEY, {
|
|
164
171
|
map,
|
|
@@ -171,9 +178,9 @@ provide(MAP_INJECTION_KEY, {
|
|
|
171
178
|
}
|
|
172
179
|
});
|
|
173
180
|
onMounted(() => {
|
|
174
|
-
watch(
|
|
181
|
+
watch(isMapReady, (v) => {
|
|
175
182
|
if (v) {
|
|
176
|
-
emits("ready",
|
|
183
|
+
emits("ready", exposed);
|
|
177
184
|
}
|
|
178
185
|
});
|
|
179
186
|
watch(status, (v) => {
|
|
@@ -191,13 +198,13 @@ onMounted(() => {
|
|
|
191
198
|
if (map.value && zoom != null)
|
|
192
199
|
map.value.setZoom(zoom);
|
|
193
200
|
});
|
|
194
|
-
watch([() => options.value.center,
|
|
201
|
+
watch([() => options.value.center, isMapReady, map], async (next) => {
|
|
195
202
|
if (!map.value) {
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
198
205
|
let center = toRaw(next[0]);
|
|
199
206
|
if (center) {
|
|
200
|
-
if (isLocationQuery(center) &&
|
|
207
|
+
if (isLocationQuery(center) && isMapReady.value) {
|
|
201
208
|
center = await resolveQueryToLatLng(center);
|
|
202
209
|
}
|
|
203
210
|
const current = map.value.getCenter();
|
|
@@ -223,9 +230,10 @@ onMounted(() => {
|
|
|
223
230
|
map.value = new mapsApi.value.Map(mapEl.value, _options);
|
|
224
231
|
if (center && isLocationQuery(center)) {
|
|
225
232
|
centerOverride.value = await resolveQueryToLatLng(center);
|
|
226
|
-
|
|
233
|
+
if (centerOverride.value)
|
|
234
|
+
map.value?.setCenter(centerOverride.value);
|
|
227
235
|
}
|
|
228
|
-
|
|
236
|
+
isMapReady.value = true;
|
|
229
237
|
});
|
|
230
238
|
});
|
|
231
239
|
if (import.meta.server) {
|
|
@@ -280,9 +288,9 @@ onBeforeUnmount(() => {
|
|
|
280
288
|
|
|
281
289
|
<template>
|
|
282
290
|
<div ref="rootEl" v-bind="rootAttrs">
|
|
283
|
-
<div v-show="
|
|
284
|
-
<slot v-if="!
|
|
285
|
-
<slot v-if="status !== 'awaitingLoad' && !
|
|
291
|
+
<div v-show="isMapReady" ref="mapEl" :style="{ width: '100%', height: '100%', maxWidth: '100%' }" />
|
|
292
|
+
<slot v-if="!isMapReady" name="placeholder" />
|
|
293
|
+
<slot v-if="status !== 'awaitingLoad' && !isMapReady" name="loading">
|
|
286
294
|
<ScriptAriaLoadingIndicator />
|
|
287
295
|
</slot>
|
|
288
296
|
<slot v-if="status === 'awaitingLoad'" name="awaitingLoad" />
|