From 0584b268836ece8eebf6f09da3ac5bc68749cbf3 Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Thu, 3 Sep 2026 14:18:23 +0800 Subject: [PATCH] fix(web): satisfy vp check for marketplace catalog prefetch PR style CI failed oxfmt on the catalog preview files. Split prefetch out of the HydrateQueryClient module so the component file only exports a component, and stop asserting extra list fields as Plugin. --- .../__tests__/server-entry.spec.tsx | 12 ++--- .../marketplace/__tests__/utils.spec.ts | 11 +++- .../plugins/marketplace/hydration-server.tsx | 51 ++----------------- .../components/plugins/marketplace/index.tsx | 3 +- .../prefetch-marketplace-dehydrated-state.ts | 46 +++++++++++++++++ .../standalone/__tests__/exports.spec.ts | 3 ++ .../plugins/marketplace/standalone/server.ts | 3 +- .../marketplace-template-discovery.spec.ts | 24 +++++---- 8 files changed, 84 insertions(+), 69 deletions(-) create mode 100644 web/app/components/plugins/marketplace/prefetch-marketplace-dehydrated-state.ts diff --git a/web/app/components/plugins/marketplace/__tests__/server-entry.spec.tsx b/web/app/components/plugins/marketplace/__tests__/server-entry.spec.tsx index f601155334e..cbb2ed377f1 100644 --- a/web/app/components/plugins/marketplace/__tests__/server-entry.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/server-entry.spec.tsx @@ -24,6 +24,9 @@ vi.mock('../home/banners', async (importOriginal) => { vi.mock('../hydration-server', () => ({ HydrateQueryClient: ({ children }: { children: ReactNode }) => children, +})) + +vi.mock('../prefetch-marketplace-dehydrated-state', () => ({ prefetchMarketplaceDehydratedState: vi.fn().mockResolvedValue(undefined), })) @@ -58,11 +61,7 @@ describe('Marketplace server entry', () => { const { default: Marketplace } = await import('../index') const element = await Marketplace({ variant: 'home' }) - render( - - {element} - , - ) + render({element}) expect(screen.getByText('Server banners: 1')).toBeInTheDocument() expect(mockGetLocaleOnServer).toHaveBeenCalledOnce() @@ -70,7 +69,8 @@ describe('Marketplace server entry', () => { }) it('starts catalog prefetch without waiting for banners to finish', async () => { - const { prefetchMarketplaceDehydratedState } = await import('../hydration-server') + const { prefetchMarketplaceDehydratedState } = + await import('../prefetch-marketplace-dehydrated-state') let resolveBanners: (banners: PluginBanner[]) => void = () => {} mockGetLocaleOnServer.mockResolvedValue('en-US') mockFetchPluginBanners.mockImplementation( diff --git a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts index 4753cebd020..a51a208fc96 100644 --- a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts +++ b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts @@ -345,7 +345,14 @@ describe('getMarketplaceCollectionsAndPlugins', () => { mockCollections.mockResolvedValueOnce({ data: { collections: [ - { name: 'featured', label: {}, description: {}, rule: '', created_at: '', updated_at: '' }, + { + name: 'featured', + label: {}, + description: {}, + rule: '', + created_at: '', + updated_at: '', + }, ], }, }) @@ -477,7 +484,7 @@ describe('toListPlugin', () => { privacy_options: 'all', readme_meta: { available_languages: ['en_US'] }, endpoint: { settings: [{ name: 'api_key' }] }, - } as Plugin + } as unknown as Plugin const listed = toListPlugin(plugin) diff --git a/web/app/components/plugins/marketplace/hydration-server.tsx b/web/app/components/plugins/marketplace/hydration-server.tsx index 6c3c88a6d8c..6fc23a6efbf 100644 --- a/web/app/components/plugins/marketplace/hydration-server.tsx +++ b/web/app/components/plugins/marketplace/hydration-server.tsx @@ -1,52 +1,7 @@ -import type { SearchParams } from 'nuqs/server' -import type { MarketplaceSearchParams } from './search-params' import type { DehydratedState } from '@tanstack/react-query' -import { dehydrate, HydrationBoundary } from '@tanstack/react-query' -import { createLoader } from 'nuqs/server' -import { getQueryClient } from '@/app/get-query-client' -import { marketplaceQuery } from '@/service/client' -import { PLUGIN_CATEGORY_WITH_COLLECTIONS } from './constants' -import { getMarketplacePluginsInfiniteQueryOptions } from './query-options' -import { - getMarketplacePluginsSearchParams, - marketplaceSearchParamsParsers, - shouldSearchMarketplacePlugins, -} from './search-params' -import { withinServerBudget } from './server-budget' -import { getCollectionsParams, getMarketplaceCollectionsAndPlugins } from './utils' - -// The server side logic should move to marketplace's codebase so that we can get rid of Next.js - -export async function prefetchMarketplaceDehydratedState(searchParams?: Promise) { - if (!searchParams) { - return - } - const loadSearchParams = createLoader(marketplaceSearchParamsParsers) - const params: MarketplaceSearchParams = await loadSearchParams(searchParams) - - const queryClient = getQueryClient() - - if (shouldSearchMarketplacePlugins(params)) { - await withinServerBudget( - queryClient.prefetchInfiniteQuery( - getMarketplacePluginsInfiniteQueryOptions(getMarketplacePluginsSearchParams(params)), - ), - ) - return dehydrate(queryClient) - } - - if (!PLUGIN_CATEGORY_WITH_COLLECTIONS.has(params.category)) return - - await withinServerBudget( - queryClient.prefetchQuery({ - queryKey: marketplaceQuery.collections.queryKey({ - input: { query: getCollectionsParams(params.category) }, - }), - queryFn: () => getMarketplaceCollectionsAndPlugins(getCollectionsParams(params.category)), - }), - ) - return dehydrate(queryClient) -} +import type { SearchParams } from 'nuqs/server' +import { HydrationBoundary } from '@tanstack/react-query' +import { prefetchMarketplaceDehydratedState } from './prefetch-marketplace-dehydrated-state' export async function HydrateQueryClient({ searchParams, diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 475c41620cf..92baa6f3380 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -3,7 +3,8 @@ import type { SearchParams } from 'nuqs' import type { MarketplaceViewProps } from './view' import { getLocaleOnServer } from '@/i18n-config/server' import { fetchPluginBanners } from './home/banners' -import { HydrateQueryClient, prefetchMarketplaceDehydratedState } from './hydration-server' +import { HydrateQueryClient } from './hydration-server' +import { prefetchMarketplaceDehydratedState } from './prefetch-marketplace-dehydrated-state' import { withinServerBudget } from './server-budget' import { MarketplaceView } from './view' diff --git a/web/app/components/plugins/marketplace/prefetch-marketplace-dehydrated-state.ts b/web/app/components/plugins/marketplace/prefetch-marketplace-dehydrated-state.ts new file mode 100644 index 00000000000..7b9924eebe9 --- /dev/null +++ b/web/app/components/plugins/marketplace/prefetch-marketplace-dehydrated-state.ts @@ -0,0 +1,46 @@ +import type { SearchParams } from 'nuqs/server' +import type { MarketplaceSearchParams } from './search-params' +import { dehydrate } from '@tanstack/react-query' +import { createLoader } from 'nuqs/server' +import { getQueryClient } from '@/app/get-query-client' +import { marketplaceQuery } from '@/service/client' +import { PLUGIN_CATEGORY_WITH_COLLECTIONS } from './constants' +import { getMarketplacePluginsInfiniteQueryOptions } from './query-options' +import { + getMarketplacePluginsSearchParams, + marketplaceSearchParamsParsers, + shouldSearchMarketplacePlugins, +} from './search-params' +import { withinServerBudget } from './server-budget' +import { getCollectionsParams, getMarketplaceCollectionsAndPlugins } from './utils' + +export async function prefetchMarketplaceDehydratedState(searchParams?: Promise) { + if (!searchParams) { + return + } + const loadSearchParams = createLoader(marketplaceSearchParamsParsers) + const params: MarketplaceSearchParams = await loadSearchParams(searchParams) + + const queryClient = getQueryClient() + + if (shouldSearchMarketplacePlugins(params)) { + await withinServerBudget( + queryClient.prefetchInfiniteQuery( + getMarketplacePluginsInfiniteQueryOptions(getMarketplacePluginsSearchParams(params)), + ), + ) + return dehydrate(queryClient) + } + + if (!PLUGIN_CATEGORY_WITH_COLLECTIONS.has(params.category)) return + + await withinServerBudget( + queryClient.prefetchQuery({ + queryKey: marketplaceQuery.collections.queryKey({ + input: { query: getCollectionsParams(params.category) }, + }), + queryFn: () => getMarketplaceCollectionsAndPlugins(getCollectionsParams(params.category)), + }), + ) + return dehydrate(queryClient) +} diff --git a/web/app/components/plugins/marketplace/standalone/__tests__/exports.spec.ts b/web/app/components/plugins/marketplace/standalone/__tests__/exports.spec.ts index 43c4d1d4b6e..7268b30e558 100644 --- a/web/app/components/plugins/marketplace/standalone/__tests__/exports.spec.ts +++ b/web/app/components/plugins/marketplace/standalone/__tests__/exports.spec.ts @@ -5,6 +5,9 @@ import { standaloneMarketplaceServer } from '../server' vi.mock('../../index', () => ({ default: () => null })) vi.mock('../../hydration-server', () => ({ HydrateQueryClient: () => null, +})) + +vi.mock('../../prefetch-marketplace-dehydrated-state', () => ({ prefetchMarketplaceDehydratedState: vi.fn(), })) diff --git a/web/app/components/plugins/marketplace/standalone/server.ts b/web/app/components/plugins/marketplace/standalone/server.ts index f5022088d2b..8e3a27a39f9 100644 --- a/web/app/components/plugins/marketplace/standalone/server.ts +++ b/web/app/components/plugins/marketplace/standalone/server.ts @@ -13,8 +13,9 @@ import { sortCreatorCreations, toPublisherSortQuery, } from '../creator-profile/model' -import { HydrateQueryClient, prefetchMarketplaceDehydratedState } from '../hydration-server' +import { HydrateQueryClient } from '../hydration-server' import Marketplace from '../index' +import { prefetchMarketplaceDehydratedState } from '../prefetch-marketplace-dehydrated-state' import { SERVER_PREFETCH_BUDGET_MS, withinServerBudget } from '../server-budget' export const standaloneMarketplaceServer = { diff --git a/web/service/marketplace-template-discovery.spec.ts b/web/service/marketplace-template-discovery.spec.ts index 0f1c00d2835..e2c910aab94 100644 --- a/web/service/marketplace-template-discovery.spec.ts +++ b/web/service/marketplace-template-discovery.spec.ts @@ -170,17 +170,19 @@ describe('marketplace template discovery', () => { }) mocks.templateCollectionTemplates.mockResolvedValue({ data: { - templates: [{ - id: 'template-1', - template_name: 'Inbox', - readme: '# long', - review_comment: 'ship it', - dsl_file_key: 'dsl.yml', - partner_link: 'https://example.com', - asset_files: [{ name: 'a' }], - asset_tree_nodes: [{ path: '/' }], - dsl_raw_file_key: 'raw.yml', - }], + templates: [ + { + id: 'template-1', + template_name: 'Inbox', + readme: '# long', + review_comment: 'ship it', + dsl_file_key: 'dsl.yml', + partner_link: 'https://example.com', + asset_files: [{ name: 'a' }], + asset_tree_nodes: [{ path: '/' }], + dsl_raw_file_key: 'raw.yml', + }, + ], }, })