From 481055ea323f3dec239404142c6a2ec8f07d4dc0 Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Wed, 1 Jul 2026 19:08:54 +0800 Subject: [PATCH 1/2] fix: avoid user package hydration mismatch --- app/composables/npm/useUserPackages.ts | 2 +- app/composables/useSettings.ts | 10 ++++++++-- app/pages/~[username]/index.vue | 2 +- test/e2e/hydration.spec.ts | 17 +++++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/composables/npm/useUserPackages.ts b/app/composables/npm/useUserPackages.ts index ed9f859382..1227461f76 100644 --- a/app/composables/npm/useUserPackages.ts +++ b/app/composables/npm/useUserPackages.ts @@ -110,7 +110,7 @@ export function useUserPackages(username: MaybeRefOrGetter) { return { ...response, isStale } }, - { default: emptySearchResponse }, + { default: emptySearchResponse, server: false }, ) // --- Fetch more (npm path only) --- /** diff --git a/app/composables/useSettings.ts b/app/composables/useSettings.ts index c1454355f3..7cfa5d12a0 100644 --- a/app/composables/useSettings.ts +++ b/app/composables/useSettings.ts @@ -1,7 +1,8 @@ import type { RemovableRef } from '@vueuse/core' +import type { LocaleObject } from '@nuxtjs/i18n' import { useLocalStorage } from '@vueuse/core' +import { onMounted, shallowRef } from 'vue' import { ACCENT_COLORS, type AccentColorId } from '#shared/utils/constants' -import type { LocaleObject } from '@nuxtjs/i18n' import { BACKGROUND_THEMES } from '#shared/utils/constants' type BackgroundThemeId = keyof typeof BACKGROUND_THEMES @@ -197,9 +198,14 @@ export function useAccentColor() { */ export function useSearchProvider() { const { settings } = useSettings() + const isMounted = shallowRef(false) + + onMounted(() => { + isMounted.value = true + }) const searchProvider = computed({ - get: () => settings.value.searchProvider, + get: () => (isMounted.value ? settings.value.searchProvider : DEFAULT_SETTINGS.searchProvider), set: (value: SearchProvider) => { settings.value.searchProvider = value }, diff --git a/app/pages/~[username]/index.vue b/app/pages/~[username]/index.vue index 5c4bb941ae..e56d7efc4e 100644 --- a/app/pages/~[username]/index.vue +++ b/app/pages/~[username]/index.vue @@ -180,7 +180,7 @@ defineOgImage( diff --git a/test/e2e/hydration.spec.ts b/test/e2e/hydration.spec.ts index 0f1c6ae9e4..528fa3aa9c 100644 --- a/test/e2e/hydration.spec.ts +++ b/test/e2e/hydration.spec.ts @@ -10,8 +10,11 @@ const PAGES = [ '/search', '/package/nuxt', '/search?q=vue', + '/~qwerzl', ] as const +const SEARCH_PROVIDER_PAGES = ['/search?q=vue', '/~qwerzl'] as const + // --------------------------------------------------------------------------- // Test matrix // @@ -113,6 +116,20 @@ test.describe('Hydration', () => { }) } }) + + // Default: "algolia" in production -> test persisted "npm" + test.describe('search provider: npm', () => { + for (const page of SEARCH_PROVIDER_PAGES) { + test(`${page}`, async ({ page: pw, goto, hydrationErrors }) => { + await injectLocalStorage(pw, { + 'npmx-settings': JSON.stringify({ searchProvider: 'npm' }), + }) + await goto(page, { waitUntil: 'hydration' }) + + expect(hydrationErrors).toEqual([]) + }) + } + }) }) async function injectLocalStorage(page: Page, entries: Record) { From 6f7418beacbe4cf02b4815992abd5c14bc792a8a Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Wed, 1 Jul 2026 21:53:00 +0800 Subject: [PATCH 2/2] refactor: use mounted helper for search provider --- app/composables/useSettings.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/composables/useSettings.ts b/app/composables/useSettings.ts index 7cfa5d12a0..55c9caadd0 100644 --- a/app/composables/useSettings.ts +++ b/app/composables/useSettings.ts @@ -1,7 +1,6 @@ import type { RemovableRef } from '@vueuse/core' import type { LocaleObject } from '@nuxtjs/i18n' -import { useLocalStorage } from '@vueuse/core' -import { onMounted, shallowRef } from 'vue' +import { useLocalStorage, useMounted } from '@vueuse/core' import { ACCENT_COLORS, type AccentColorId } from '#shared/utils/constants' import { BACKGROUND_THEMES } from '#shared/utils/constants' @@ -198,11 +197,7 @@ export function useAccentColor() { */ export function useSearchProvider() { const { settings } = useSettings() - const isMounted = shallowRef(false) - - onMounted(() => { - isMounted.value = true - }) + const isMounted = useMounted() const searchProvider = computed({ get: () => (isMounted.value ? settings.value.searchProvider : DEFAULT_SETTINGS.searchProvider),