Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/react-query-operation-dispatch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kubb/plugin-react-query": patch
---

Classify each operation as query or mutation once and dispatch only to the matching hook generators, instead of running all five (query, suspenseQuery, infiniteQuery, suspenseInfiniteQuery, mutation) for every operation with four returning early. Fixes kubb-labs/kubb#3816. Generated output is unchanged.
1 change: 1 addition & 0 deletions internals/tanstack-query/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { InfiniteQueryOptions } from './components/InfiniteQueryOptions.tsx'
export { MutationKey, mutationKeyTransformer } from './components/MutationKey.tsx'
export { createOperationDispatcher } from './operationDispatcher.ts'
export { createMutationResolver, createQueryResolver, type MutationResolver, type QueryResolver, type QueryVariant } from './resolver.ts'
export { QueryKey, queryKeyTransformer } from './components/QueryKey.tsx'
export type { Infinite, Mutation, ParamsCasing, ParamsType, PathParamsType, Query, Transformer } from './types.ts'
Expand Down
51 changes: 51 additions & 0 deletions internals/tanstack-query/src/operationDispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ast, defineGenerator } from 'kubb/kit'
import { jsxRenderer } from 'kubb/jsx'
import { classifyOperation } from './utils.ts'
import type { Generator, PluginFactoryOptions } from 'kubb/kit'
import type { KubbReactElement } from 'kubb/jsx'
import type { Mutation, Query } from './types.ts'

type OperationDispatcherResolvedOptions = {
query: Required<Query> | false
mutation: Required<Mutation> | false
}

/**
* Builds a single operation generator that classifies each operation as query or mutation once,
* then renders only the generators for that family. Replaces registering every hook generator
* (query, suspenseQuery, infiniteQuery, suspenseInfiniteQuery, mutation, ...) directly with the
* core engine, which would otherwise run all of them per operation with the non-matching ones
* returning early. No `renderer` is set on the returned generator: each matching generator's
* JSX result is rendered and upserted directly, so callers stay plain TypeScript.
*/
export function createOperationDispatcher<TOptions extends PluginFactoryOptions<string, object, OperationDispatcherResolvedOptions>>({
name,
queryGenerators,
mutationGenerators,
}: {
name: string
queryGenerators: Array<Generator<TOptions>>
mutationGenerators: Array<Generator<TOptions>>
}): Generator<TOptions> {
return defineGenerator<TOptions>({
name,
async operation(node, ctx) {
if (!ast.isHttpOperationNode(node)) return null
const { query, mutation } = ctx.options

const { isQuery, isMutation } = classifyOperation(node, { query, mutation })
const family = isMutation ? mutationGenerators : isQuery ? queryGenerators : []

for (const generator of family) {
const element = await generator.operation?.(node, ctx)
if (!element) continue

using instance = jsxRenderer()
await instance.render(element as KubbReactElement)
await ctx.upsertFile(...instance.files)
}

return null
},
})
}
25 changes: 15 additions & 10 deletions packages/plugin-react-query/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { createGroupConfig } from '@internals/shared'
import { definePlugin, Resolver } from 'kubb/kit'
import { resolveContractClient } from '@internals/client'
import { pluginTsName } from '@kubb/plugin-ts'
import { mutationKeyTransformer, queryKeyTransformer, resolveInfiniteConfig, resolveMutationConfig, resolveQueryConfig } from '@internals/tanstack-query'
import {
createOperationDispatcher,
mutationKeyTransformer,
queryKeyTransformer,
resolveInfiniteConfig,
resolveMutationConfig,
resolveQueryConfig,
} from '@internals/tanstack-query'
import {
customHookOptionsFileGenerator,
hookOptionsGenerator,
Expand All @@ -15,6 +22,12 @@ import {
import { resolverReactQuery } from './resolvers/resolverReactQuery.ts'
import type { PluginReactQuery, ResolverReactQuery } from './types.ts'

const operationGenerator = createOperationDispatcher<PluginReactQuery>({
name: 'react-query-operation',
queryGenerators: [queryGenerator, suspenseQueryGenerator, infiniteQueryGenerator, suspenseInfiniteQueryGenerator],
mutationGenerators: [mutationGenerator],
})

/**
* Canonical plugin name for `@kubb/plugin-react-query`. Used for driver lookups
* and cross-plugin dependency references.
Expand Down Expand Up @@ -66,15 +79,7 @@ export const pluginReactQuery = definePlugin<PluginReactQuery>((options) => {
macros: userMacros,
} = options

const selectedGenerators = [
queryGenerator,
suspenseQueryGenerator,
infiniteQueryGenerator,
suspenseInfiniteQueryGenerator,
mutationGenerator,
hookOptionsGenerator,
customHookOptionsFileGenerator,
]
const selectedGenerators = [operationGenerator, hookOptionsGenerator, customHookOptionsFileGenerator]

const groupConfig = createGroupConfig(group)

Expand Down
Loading