From 682d752cdacfb45c9e980d9442b2901f0dda8983 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Thu, 19 Mar 2026 14:38:22 +1100 Subject: [PATCH 01/10] fix: prevent memory leaks in all Google Maps sub-components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix (#649) only handled one race condition — unmounting during `await importLibrary`. The actual leak was caused by orphaned Vue watchers: watchers created after an `await` lose component instance context and are never auto-stopped on unmount, retaining the entire reactive scope. - Create `useGoogleMapsResource` composable encoding lifecycle safety - Move all options watchers to synchronous setup scope (Vue auto-stops) - Add unmount guards to all async resource creation callbacks - Null all resource refs on unmount to release Google Maps objects - Convert `let` variables to `shallowRef` for proper GC - Make parent `onBeforeUnmount` synchronous (Vue doesn't await async hooks) - Clear `libraries` and `queryToLatLngCache` on parent unmount - Extract injection keys to shared `injectionKeys.ts` Closes #646 --- .../GoogleMaps/ScriptGoogleMaps.vue | 45 +- .../ScriptGoogleMapsAdvancedMarkerElement.vue | 89 +-- .../GoogleMaps/ScriptGoogleMapsCircle.vue | 54 +- .../ScriptGoogleMapsHeatmapLayer.vue | 44 +- .../GoogleMaps/ScriptGoogleMapsInfoWindow.vue | 66 +- .../GoogleMaps/ScriptGoogleMapsMarker.vue | 85 +-- .../ScriptGoogleMapsMarkerClusterer.vue | 46 +- .../GoogleMaps/ScriptGoogleMapsPinElement.vue | 55 +- .../GoogleMaps/ScriptGoogleMapsPolygon.vue | 54 +- .../GoogleMaps/ScriptGoogleMapsPolyline.vue | 54 +- .../GoogleMaps/ScriptGoogleMapsRectangle.vue | 54 +- .../components/GoogleMaps/injectionKeys.ts | 14 + .../GoogleMaps/useGoogleMapsResource.ts | 66 ++ .../google-maps-lifecycle.nuxt.test.ts | 705 ++++++++++++++++++ test/unit/__mocks__/google-maps-api.ts | 76 ++ test/unit/google-maps-lifecycle.test.ts | 370 +++++++++ 16 files changed, 1475 insertions(+), 402 deletions(-) create mode 100644 src/runtime/components/GoogleMaps/injectionKeys.ts create mode 100644 src/runtime/components/GoogleMaps/useGoogleMapsResource.ts create mode 100644 test/nuxt-runtime/google-maps-lifecycle.nuxt.test.ts create mode 100644 test/unit/google-maps-lifecycle.test.ts diff --git a/src/runtime/components/GoogleMaps/ScriptGoogleMaps.vue b/src/runtime/components/GoogleMaps/ScriptGoogleMaps.vue index 1e865c6ff..e0d6cd902 100644 --- a/src/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +++ b/src/runtime/components/GoogleMaps/ScriptGoogleMaps.vue @@ -2,7 +2,7 @@ /// import type { ElementScriptTrigger } from '#nuxt-scripts/types' import type { QueryObject } from 'ufo' -import type { HTMLAttributes, ImgHTMLAttributes, InjectionKey, Ref, ReservedProps, ShallowRef } from 'vue' +import type { HTMLAttributes, ImgHTMLAttributes, Ref, ReservedProps, ShallowRef } from 'vue' import { useScriptTriggerElement } from '#nuxt-scripts/composables/useScriptTriggerElement' import { useScriptGoogleMaps } from '#nuxt-scripts/registry/google-maps' import { scriptRuntimeConfig } from '#nuxt-scripts/utils' @@ -13,10 +13,9 @@ import { withQuery } from 'ufo' import { computed, onBeforeUnmount, onMounted, provide, ref, shallowRef, toRaw, watch } from 'vue' import ScriptAriaLoadingIndicator from '../ScriptAriaLoadingIndicator.vue' -export const MAP_INJECTION_KEY = Symbol('map') as InjectionKey<{ - map: ShallowRef - mapsApi: Ref -}> +import { MAP_INJECTION_KEY } from './injectionKeys' + +export { MAP_INJECTION_KEY } from './injectionKeys' diff --git a/src/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue b/src/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue index 2aaa65c0d..8cbdbd8d6 100644 --- a/src/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue +++ b/src/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue @@ -1,13 +1,10 @@ diff --git a/src/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue b/src/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue index 63aa2e9af..163687d8d 100644 --- a/src/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue +++ b/src/runtime/components/GoogleMaps/ScriptGoogleMapsCircle.vue @@ -1,7 +1,6 @@ diff --git a/src/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue b/src/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue index 58c35fb14..5903da59c 100644 --- a/src/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue +++ b/src/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue @@ -1,37 +1,29 @@