@7nohe/openapi-react-query-codegen
Advanced tools
| import type { GenerationContext, OperationInfo } from "../types.mjs"; | ||
| /** | ||
| * Resolve the generated Data type name for an operation, falling back to | ||
| * unknown when the SDK signature exposes no Data type. | ||
| * See OperationInfo.dataTypeName for why the name is read from the signature | ||
| * instead of derived from the method name (#213). | ||
| */ | ||
| export declare function getDataTypeName(op: OperationInfo): string; | ||
| /** | ||
| * Get the error type string based on client type. | ||
| * The Error type shares its stem with the Data type — hey-api mints both from | ||
| * the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes | ||
| * from `dataTypeName` rather than the method name. The modelNames probe stays | ||
| * because operations without error responses have no generated Error type. | ||
| */ | ||
| export declare function getErrorType(op: OperationInfo, ctx: GenerationContext): string; |
| /** | ||
| * Resolve the generated Data type name for an operation, falling back to | ||
| * unknown when the SDK signature exposes no Data type. | ||
| * See OperationInfo.dataTypeName for why the name is read from the signature | ||
| * instead of derived from the method name (#213). | ||
| */ | ||
| export function getDataTypeName(op) { | ||
| return op.dataTypeName ?? "unknown"; | ||
| } | ||
| /** | ||
| * Get the error type string based on client type. | ||
| * The Error type shares its stem with the Data type — hey-api mints both from | ||
| * the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes | ||
| * from `dataTypeName` rather than the method name. The modelNames probe stays | ||
| * because operations without error responses have no generated Error type. | ||
| */ | ||
| export function getErrorType(op, ctx) { | ||
| const stem = op.dataTypeName | ||
| ? op.dataTypeName.replace(/Data$/, "") | ||
| : op.capitalizedMethodName; | ||
| const errorTypeName = `${stem}Error`; | ||
| const errorType = ctx.modelNames.includes(errorTypeName) | ||
| ? errorTypeName | ||
| : "unknown"; | ||
| if (ctx.client === "@hey-api/client-axios") { | ||
| return `AxiosError<${errorType}>`; | ||
| } | ||
| return errorType; | ||
| } |
@@ -1,2 +0,2 @@ | ||
| import type { Project } from "ts-morph"; | ||
| import { type Project } from "ts-morph"; | ||
| import type { GenerationContext, OperationInfo } from "./types.mjs"; | ||
@@ -3,0 +3,0 @@ /** |
@@ -0,1 +1,2 @@ | ||
| import { Node, } from "ts-morph"; | ||
| import ts from "typescript"; | ||
@@ -39,4 +40,21 @@ import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromVariable, getShortType, getVariableArrowFunctionParameters, } from "./common.mjs"; | ||
| /** | ||
| * Get paginatable methods by checking if their Data type has the pageParam in query property. | ||
| * Read the operation's Data type name from the first type argument of the | ||
| * SDK options parameter (`Options<XData, ThrowOnError>`). | ||
| * See OperationInfo.dataTypeName for why the name is read from the signature | ||
| * instead of derived from the method name (#213). | ||
| */ | ||
| function getDataTypeNameFromSignature(optionsParam) { | ||
| const typeNode = optionsParam?.getTypeNode(); | ||
| if (!typeNode || !Node.isTypeReference(typeNode)) | ||
| return undefined; | ||
| if (typeNode.getTypeName().getText() !== "Options") | ||
| return undefined; | ||
| const [dataTypeArg] = typeNode.getTypeArguments(); | ||
| return dataTypeArg?.getText(); | ||
| } | ||
| /** | ||
| * Get paginatable Data types by checking if they have the pageParam in their query property. | ||
| * Uses TypeScript compiler API for accurate AST traversal. | ||
| * The map is keyed by the Data type name (e.g., "FindPetsData") so callers can | ||
| * look it up with the name read from the SDK signature. | ||
| */ | ||
@@ -74,9 +92,5 @@ function getPaginatableMethods(project, pageParam) { | ||
| if (pageParamNode) { | ||
| // Extract method name from Data type name (e.g., "FindPetsData" -> "findPets") | ||
| const methodName = key.slice(0, -4); // Remove "Data" suffix | ||
| // Convert first letter to lowercase | ||
| const methodNameLower = methodName.charAt(0).toLowerCase() + methodName.slice(1); | ||
| const pageParamType = pageParamNode.type?.getText(modelsFile.compilerNode); | ||
| const resolvedType = typeChecker.getTypeAtLocation(pageParamNode.type ?? pageParamNode); | ||
| paginatableMethods.set(methodNameLower, { | ||
| paginatableMethods.set(key, { | ||
| type: pageParamType ?? "unknown", | ||
@@ -105,3 +119,6 @@ typeKind: getPageParamTypeKind(resolvedType), | ||
| const allParamsOptional = sdkParams.length === 0 || sdkParams[0].isOptional(); | ||
| const pageParamInfo = paginatableMethods.get(methodName); | ||
| const dataTypeName = getDataTypeNameFromSignature(sdkParams[0]); | ||
| const pageParamInfo = dataTypeName | ||
| ? paginatableMethods.get(dataTypeName) | ||
| : undefined; | ||
| const isPaginatable = httpMethod === "GET" && pageParamInfo !== undefined; | ||
@@ -111,2 +128,3 @@ return { | ||
| capitalizedMethodName: capitalizeFirstLetter(methodName), | ||
| dataTypeName, | ||
| httpMethod, | ||
@@ -113,0 +131,0 @@ jsDoc: desc.jsDoc, |
@@ -33,3 +33,3 @@ import { type TypeAliasDeclarationStructure, type VariableStatementStructure } from "ts-morph"; | ||
| */ | ||
| export declare function buildQueryKeyFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure; | ||
| export declare function buildQueryKeyFn(op: OperationInfo): VariableStatementStructure; | ||
| /** | ||
@@ -36,0 +36,0 @@ * Build mutation key function. |
| import { StructureKind, VariableDeclarationKind, } from "ts-morph"; | ||
| import { getDataTypeName } from "./operationNames.mjs"; | ||
| /** | ||
@@ -81,6 +82,4 @@ * Build the default response type alias. | ||
| */ | ||
| export function buildQueryKeyFn(op, ctx) { | ||
| const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`) | ||
| ? `${op.capitalizedMethodName}Data` | ||
| : "unknown"; | ||
| export function buildQueryKeyFn(op) { | ||
| const dataTypeName = getDataTypeName(op); | ||
| const params = []; | ||
@@ -130,8 +129,4 @@ const defaultValue = op.allParamsOptional ? " = {}" : ""; | ||
| export function buildInfiniteClientOptionsType(op, ctx) { | ||
| const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`) | ||
| ? `${op.capitalizedMethodName}Data` | ||
| : "unknown"; | ||
| const type = dataTypeName === "unknown" | ||
| ? "Options<unknown, true>" | ||
| : `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`; | ||
| const dataTypeName = getDataTypeName(op); | ||
| const type = `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`; | ||
| return { | ||
@@ -138,0 +133,0 @@ kind: StructureKind.TypeAlias, |
| import { StructureKind, VariableDeclarationKind, } from "ts-morph"; | ||
| import { SDK_CALL_ARGS } from "./buildQueryHooks.mjs"; | ||
| import { getDataTypeName, getErrorType } from "./operationNames.mjs"; | ||
| /** | ||
| * Get the error type string based on client type. | ||
| */ | ||
| function getErrorType(op, ctx) { | ||
| const errorTypeName = `${op.capitalizedMethodName}Error`; | ||
| // Operations without error responses have no generated Error type | ||
| const errorType = ctx.modelNames.includes(errorTypeName) | ||
| ? errorTypeName | ||
| : "unknown"; | ||
| if (ctx.client === "@hey-api/client-axios") { | ||
| return `AxiosError<${errorType}>`; | ||
| } | ||
| return errorType; | ||
| } | ||
| /** | ||
| * Build useMutation hook. | ||
@@ -33,6 +20,3 @@ * Example: | ||
| const dataTypeDefault = `Common.${op.capitalizedMethodName}MutationResult`; | ||
| const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`) | ||
| ? `${op.capitalizedMethodName}Data` | ||
| : "unknown"; | ||
| const optionsType = `Options<${dataTypeName}, true>`; | ||
| const optionsType = `Options<${getDataTypeName(op)}, true>`; | ||
| const mutationFn = `clientOptions => ${op.methodName}(${SDK_CALL_ARGS}) as unknown as Promise<TData>`; | ||
@@ -39,0 +23,0 @@ const body = `useMutation<TData, TError, ${optionsType}, TContext>({ mutationKey: Common.Use${op.capitalizedMethodName}KeyFn(mutationKey), mutationFn: ${mutationFn}, ...options })`; |
| import { type VariableStatementStructure } from "ts-morph"; | ||
| import type { GenerationContext, OperationInfo } from "../types.mjs"; | ||
| /** | ||
| * Resolve the generated Data type name for an operation, falling back to | ||
| * unknown when the operation has no generated Data type. | ||
| */ | ||
| export declare function getDataTypeName(op: OperationInfo, ctx: GenerationContext): string; | ||
| /** | ||
| * SDK call arguments shared by every generated queryFn/mutationFn. | ||
@@ -22,3 +17,3 @@ * throwOnError: true forces the SDK call to reject on error responses; the | ||
| */ | ||
| export declare function buildClientOptionsParam(op: OperationInfo, ctx: GenerationContext): string; | ||
| export declare function buildClientOptionsParam(op: OperationInfo): string; | ||
| /** | ||
@@ -31,5 +26,16 @@ * Build the clientOptions parameter typed with the page-less infinite | ||
| /** | ||
| * The type of a single page. TanStack Query instantiates the infinite options | ||
| * with this as TQueryFnData, so it is what `getNextPageParam` receives as | ||
| * `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved | ||
| * page is always present. | ||
| */ | ||
| export declare function getPageType(op: OperationInfo): string; | ||
| /** | ||
| * Build the paginated SDK call shared by every infinite query builder. | ||
| * The resolved response is narrowed to the page type so it matches the | ||
| * TQueryFnData the infinite options are instantiated with (#203). Spelled as | ||
| * a cast rather than `!` because generated code is linted downstream, and a | ||
| * non-null assertion trips biome's noNonNullAssertion. | ||
| */ | ||
| export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext, castTData: boolean): string; | ||
| export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext): string; | ||
| /** | ||
@@ -56,4 +62,11 @@ * Format the initialPageParam literal. Emits `undefined` when the caller opted | ||
| * callers may replace them for custom pagination schemes (#156, #146). | ||
| * | ||
| * The first type argument is TQueryFnData — a single page — not TData (#203). | ||
| * Passing TData there made `getNextPageParam` receive the aggregated | ||
| * `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to | ||
| * compile. Only the first three type arguments are written: TanStack Query | ||
| * dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so | ||
| * positions beyond TData are not stable across the supported peer range. | ||
| */ | ||
| export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string): string; | ||
| export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string, pageType: string): string; | ||
| /** | ||
@@ -95,10 +108,10 @@ * Build useQuery hook. | ||
| */ | ||
| export declare function buildPrefetchFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure; | ||
| export declare function buildPrefetchFn(op: OperationInfo): VariableStatementStructure; | ||
| /** | ||
| * Build prefetchInfiniteQuery function for a paginatable operation. | ||
| * Example: | ||
| * export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) => | ||
| * export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) => | ||
| * queryClient.prefetchInfiniteQuery({ | ||
| * queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>), | ||
| * initialPageParam: 1, | ||
@@ -120,2 +133,2 @@ * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage, | ||
| */ | ||
| export declare function buildEnsureQueryDataFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure; | ||
| export declare function buildEnsureQueryDataFn(op: OperationInfo): VariableStatementStructure; |
| import { StructureKind, VariableDeclarationKind, } from "ts-morph"; | ||
| import { getDataTypeName, getErrorType } from "./operationNames.mjs"; | ||
| /** | ||
| * Get the error type string based on client type. | ||
| */ | ||
| function getErrorType(op, ctx) { | ||
| const errorTypeName = `${op.capitalizedMethodName}Error`; | ||
| // Operations without error responses have no generated Error type | ||
| const errorType = ctx.modelNames.includes(errorTypeName) | ||
| ? errorTypeName | ||
| : "unknown"; | ||
| if (ctx.client === "@hey-api/client-axios") { | ||
| return `AxiosError<${errorType}>`; | ||
| } | ||
| return errorType; | ||
| } | ||
| /** | ||
| * Resolve the generated Data type name for an operation, falling back to | ||
| * unknown when the operation has no generated Data type. | ||
| */ | ||
| export function getDataTypeName(op, ctx) { | ||
| return ctx.modelNames.includes(`${op.capitalizedMethodName}Data`) | ||
| ? `${op.capitalizedMethodName}Data` | ||
| : "unknown"; | ||
| } | ||
| /** | ||
| * SDK call arguments shared by every generated queryFn/mutationFn. | ||
@@ -41,4 +19,4 @@ * throwOnError: true forces the SDK call to reject on error responses; the | ||
| */ | ||
| export function buildClientOptionsParam(op, ctx) { | ||
| const dataTypeName = getDataTypeName(op, ctx); | ||
| export function buildClientOptionsParam(op) { | ||
| const dataTypeName = getDataTypeName(op); | ||
| const hasParams = op.parameters.length > 0; | ||
@@ -61,9 +39,20 @@ if (!hasParams) { | ||
| /** | ||
| * The type of a single page. TanStack Query instantiates the infinite options | ||
| * with this as TQueryFnData, so it is what `getNextPageParam` receives as | ||
| * `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved | ||
| * page is always present. | ||
| */ | ||
| export function getPageType(op) { | ||
| return `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`; | ||
| } | ||
| /** | ||
| * Build the paginated SDK call shared by every infinite query builder. | ||
| * The resolved response is narrowed to the page type so it matches the | ||
| * TQueryFnData the infinite options are instantiated with (#203). Spelled as | ||
| * a cast rather than `!` because generated code is linted downstream, and a | ||
| * non-null assertion trips biome's noNonNullAssertion. | ||
| */ | ||
| export function buildPagedQueryFn(op, ctx, castTData) { | ||
| const dataTypeName = getDataTypeName(op, ctx); | ||
| const thenClause = castTData | ||
| ? ".then(response => response.data as TData) as TData" | ||
| : ".then(response => response.data)"; | ||
| export function buildPagedQueryFn(op, ctx) { | ||
| const dataTypeName = getDataTypeName(op); | ||
| const thenClause = `.then(response => response.data as ${getPageType(op)})`; | ||
| const pageParamType = getPageParamType(op); | ||
@@ -121,5 +110,12 @@ // When the initial page param is omitted, the first request must send no | ||
| * callers may replace them for custom pagination schemes (#156, #146). | ||
| * | ||
| * The first type argument is TQueryFnData — a single page — not TData (#203). | ||
| * Passing TData there made `getNextPageParam` receive the aggregated | ||
| * `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to | ||
| * compile. Only the first three type arguments are written: TanStack Query | ||
| * dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so | ||
| * positions beyond TData are not stable across the supported peer range. | ||
| */ | ||
| export function buildOverridableInfiniteOptionsType(optionsTypeName) { | ||
| const instantiated = `${optionsTypeName}<TData, TError>`; | ||
| export function buildOverridableInfiniteOptionsType(optionsTypeName, pageType) { | ||
| const instantiated = `${optionsTypeName}<${pageType}, TError, TData>`; | ||
| return `Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<${instantiated}, "initialPageParam" | "getNextPageParam">>`; | ||
@@ -144,3 +140,3 @@ } | ||
| const dataTypeDefault = `Common.${op.capitalizedMethodName}DefaultResponse`; | ||
| const clientOptionsParam = buildClientOptionsParam(op, ctx); | ||
| const clientOptionsParam = buildClientOptionsParam(op); | ||
| const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`; | ||
@@ -169,3 +165,3 @@ const body = `useQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`; | ||
| const dataTypeDefault = `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`; | ||
| const clientOptionsParam = buildClientOptionsParam(op, ctx); | ||
| const clientOptionsParam = buildClientOptionsParam(op); | ||
| const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`; | ||
@@ -209,3 +205,3 @@ const body = `useSuspenseQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`; | ||
| : `InfiniteData<${baseDataType}>`; | ||
| const queryFn = buildPagedQueryFn(op, ctx, true); | ||
| const queryFn = buildPagedQueryFn(op, ctx); | ||
| const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`; | ||
@@ -222,3 +218,3 @@ const body = `${hookCall}({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`; | ||
| name: hookName, | ||
| initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName)}) => ${body}`, | ||
| initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName, getPageType(op))}) => ${body}`, | ||
| }, | ||
@@ -250,3 +246,3 @@ ], | ||
| */ | ||
| export function buildPrefetchFn(op, ctx) { | ||
| export function buildPrefetchFn(op) { | ||
| const fnName = `prefetchUse${op.capitalizedMethodName}`; | ||
@@ -265,3 +261,3 @@ const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`; | ||
| name: fnName, | ||
| initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`, | ||
| initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`, | ||
| }, | ||
@@ -274,6 +270,6 @@ ], | ||
| * Example: | ||
| * export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) => | ||
| * export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) => | ||
| * queryClient.prefetchInfiniteQuery({ | ||
| * queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>), | ||
| * initialPageParam: 1, | ||
@@ -289,5 +285,13 @@ * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage, | ||
| const fnName = `prefetchUse${op.capitalizedMethodName}Infinite`; | ||
| const queryFn = buildPagedQueryFn(op, ctx, false); | ||
| const queryFn = buildPagedQueryFn(op, ctx); | ||
| const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`; | ||
| const optionsParam = `options?: Omit<FetchInfiniteQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">`; | ||
| // The pagination fields are re-added as optional overrides: `...options` is | ||
| // spread after the defaults, so replacing them works at runtime and must not | ||
| // be forbidden by the type (#203). getNextPageParam is spelled out rather | ||
| // than Picked, because FetchInfiniteQueryOptions only exposes it on the | ||
| // union member that also requires `pages`. | ||
| const pageType = getPageType(op); | ||
| const instantiated = `FetchInfiniteQueryOptions<${pageType}>`; | ||
| const overrides = `Partial<Pick<${instantiated}, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, ${pageType}> }`; | ||
| const optionsParam = `options?: Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & ${overrides}`; | ||
| const body = `queryClient.prefetchInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`; | ||
@@ -318,3 +322,3 @@ return { | ||
| */ | ||
| export function buildEnsureQueryDataFn(op, ctx) { | ||
| export function buildEnsureQueryDataFn(op) { | ||
| const fnName = `ensureUse${op.capitalizedMethodName}Data`; | ||
@@ -333,3 +337,3 @@ const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`; | ||
| name: fnName, | ||
| initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`, | ||
| initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`, | ||
| }, | ||
@@ -336,0 +340,0 @@ ], |
@@ -15,3 +15,3 @@ import { type VariableStatementStructure } from "ts-morph"; | ||
| */ | ||
| export declare function buildQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure; | ||
| export declare function buildQueryOptionsFn(op: OperationInfo): VariableStatementStructure; | ||
| /** | ||
@@ -21,10 +21,11 @@ * Build an infiniteQueryOptions factory for a paginatable GET operation. | ||
| * Example: | ||
| * export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) => | ||
| * export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) => | ||
| * infiniteQueryOptions({ | ||
| * queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>), | ||
| * initialPageParam: 1, | ||
| * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage, | ||
| * ...options, | ||
| * }); | ||
| */ | ||
| export declare function buildInfiniteQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null; |
| import { StructureKind, VariableDeclarationKind, } from "ts-morph"; | ||
| import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, QUERY_SDK_CALL_ARGS, } from "./buildQueryHooks.mjs"; | ||
| import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, getPageType, QUERY_SDK_CALL_ARGS, } from "./buildQueryHooks.mjs"; | ||
| /** | ||
@@ -15,5 +15,5 @@ * Build a queryOptions factory for a GET operation. | ||
| */ | ||
| export function buildQueryOptionsFn(op, ctx) { | ||
| export function buildQueryOptionsFn(op) { | ||
| const fnName = `${op.methodName}Options`; | ||
| const clientOptionsParam = buildClientOptionsParam(op, ctx); | ||
| const clientOptionsParam = buildClientOptionsParam(op); | ||
| const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`; | ||
@@ -39,8 +39,9 @@ const body = `queryOptions({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn} })`; | ||
| * Example: | ||
| * export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) => | ||
| * export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) => | ||
| * infiniteQueryOptions({ | ||
| * queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data), | ||
| * queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>), | ||
| * initialPageParam: 1, | ||
| * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage, | ||
| * ...options, | ||
| * }); | ||
@@ -53,5 +54,9 @@ */ | ||
| const fnName = `${op.methodName}InfiniteOptions`; | ||
| const queryFn = buildPagedQueryFn(op, ctx, false); | ||
| const queryFn = buildPagedQueryFn(op, ctx); | ||
| const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`; | ||
| const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions} })`; | ||
| // Only the pagination fields are overridable here: the factory's return type | ||
| // is what every downstream consumer infers from, and a wider options type | ||
| // (select, placeholderData, ...) would make that inference ambiguous (#203). | ||
| const optionsParam = `options?: Partial<Pick<UseInfiniteQueryOptions<${getPageType(op)}>, "initialPageParam" | "getNextPageParam">>`; | ||
| const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`; | ||
| return { | ||
@@ -66,3 +71,3 @@ kind: StructureKind.VariableStatement, | ||
| name: fnName, | ||
| initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>) => ${body}`, | ||
| initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>, ${optionsParam}) => ${body}`, | ||
| }, | ||
@@ -69,0 +74,0 @@ ], |
@@ -6,28 +6,18 @@ import { OpenApiRqFiles } from "../constants.mjs"; | ||
| import { buildInfiniteQueryOptionsFn, buildQueryOptionsFn, } from "./buildQueryOptions.mjs"; | ||
| import { buildAxiosErrorImport, buildClientImport, buildCommonImport, buildModelImport, buildQueryImport, buildQueryOptionsImport, buildServiceImport, createGenerationProject, } from "./projectFactory.mjs"; | ||
| import { buildCommonFileImports, buildHookFileImports, buildQueryOptionsFileImports, createGenerationProject, } from "./projectFactory.mjs"; | ||
| /** | ||
| * Build imports for common.ts file. | ||
| * Add one variable statement per operation, skipping the ones the builder | ||
| * declines. A builder returns null when the operation is out of its scope — | ||
| * every infinite-query builder returns null for non-paginatable operations, | ||
| * which is how the paginatable subset gets selected. | ||
| */ | ||
| function buildCommonFileImports(ctx) { | ||
| const imports = [ | ||
| buildClientImport(ctx), | ||
| buildQueryImport(), | ||
| buildServiceImport(ctx), | ||
| ]; | ||
| const modelImport = buildModelImport(ctx); | ||
| if (modelImport) { | ||
| imports.push(modelImport); | ||
| function addStatements(sourceFile, operations, build) { | ||
| for (const op of operations) { | ||
| const statement = build(op); | ||
| if (statement) { | ||
| sourceFile.addVariableStatement(statement); | ||
| } | ||
| } | ||
| if (ctx.client === "@hey-api/client-axios") { | ||
| imports.push(buildAxiosErrorImport()); | ||
| } | ||
| return imports; | ||
| } | ||
| /** | ||
| * Build imports for hook files (queries, suspense, infinite, prefetch, ensure). | ||
| */ | ||
| function buildHookFileImports(ctx) { | ||
| return [buildCommonImport(), ...buildCommonFileImports(ctx)]; | ||
| } | ||
| /** | ||
| * Generate the index.ts file content. | ||
@@ -55,3 +45,3 @@ * The content is constant, so no ts-morph project is needed. | ||
| sourceFile.addVariableStatement(buildQueryKeyConst(op)); | ||
| sourceFile.addVariableStatement(buildQueryKeyFn(op, ctx)); | ||
| sourceFile.addVariableStatement(buildQueryKeyFn(op)); | ||
| } | ||
@@ -100,24 +90,10 @@ // Add dedicated infinite query types and keys for paginatable operations | ||
| // Add imports | ||
| const imports = [ | ||
| buildCommonImport(), | ||
| buildQueryOptionsImport(), | ||
| buildClientImport(ctx), | ||
| buildServiceImport(ctx), | ||
| ]; | ||
| const modelImport = buildModelImport(ctx); | ||
| if (modelImport) { | ||
| imports.push(modelImport); | ||
| } | ||
| sourceFile.addImportDeclarations(imports); | ||
| sourceFile.addImportDeclarations(buildQueryOptionsFileImports(ctx)); | ||
| // Only GET operations have query options | ||
| const getOperations = operations.filter((op) => op.httpMethod === "GET"); | ||
| for (const op of getOperations) { | ||
| sourceFile.addVariableStatement(buildQueryOptionsFn(op, ctx)); | ||
| sourceFile.addVariableStatement(buildQueryOptionsFn(op)); | ||
| } | ||
| for (const op of getOperations) { | ||
| const infiniteOptions = buildInfiniteQueryOptionsFn(op, ctx); | ||
| if (infiniteOptions) { | ||
| sourceFile.addVariableStatement(infiniteOptions); | ||
| } | ||
| } | ||
| // Add infiniteQueryOptions factories | ||
| addStatements(sourceFile, getOperations, (op) => buildInfiniteQueryOptionsFn(op, ctx)); | ||
| return sourceFile.getFullText(); | ||
@@ -139,9 +115,4 @@ } | ||
| } | ||
| // Add useSuspenseInfiniteQuery hooks for paginatable operations | ||
| for (const op of getOperations.filter((o) => o.isPaginatable)) { | ||
| const hook = buildUseSuspenseInfiniteQueryHook(op, ctx); | ||
| if (hook) { | ||
| sourceFile.addVariableStatement(hook); | ||
| } | ||
| } | ||
| // Add useSuspenseInfiniteQuery hooks | ||
| addStatements(sourceFile, getOperations, (op) => buildUseSuspenseInfiniteQueryHook(op, ctx)); | ||
| return sourceFile.getFullText(); | ||
@@ -157,11 +128,6 @@ } | ||
| sourceFile.addImportDeclarations(buildHookFileImports(ctx)); | ||
| // Only paginatable GET operations | ||
| const paginatableOperations = operations.filter((op) => op.httpMethod === "GET" && op.isPaginatable); | ||
| // Only GET operations can be paginatable | ||
| const getOperations = operations.filter((op) => op.httpMethod === "GET"); | ||
| // Add useInfiniteQuery hooks | ||
| for (const op of paginatableOperations) { | ||
| const hook = buildUseInfiniteQueryHook(op, ctx); | ||
| if (hook) { | ||
| sourceFile.addVariableStatement(hook); | ||
| } | ||
| } | ||
| addStatements(sourceFile, getOperations, (op) => buildUseInfiniteQueryHook(op, ctx)); | ||
| return sourceFile.getFullText(); | ||
@@ -181,11 +147,6 @@ } | ||
| for (const op of getOperations) { | ||
| sourceFile.addVariableStatement(buildPrefetchFn(op, ctx)); | ||
| sourceFile.addVariableStatement(buildPrefetchFn(op)); | ||
| } | ||
| // Add prefetchInfiniteQuery functions for paginatable operations | ||
| for (const op of getOperations.filter((o) => o.isPaginatable)) { | ||
| const fn = buildPrefetchInfiniteQueryFn(op, ctx); | ||
| if (fn) { | ||
| sourceFile.addVariableStatement(fn); | ||
| } | ||
| } | ||
| // Add prefetchInfiniteQuery functions | ||
| addStatements(sourceFile, getOperations, (op) => buildPrefetchInfiniteQueryFn(op, ctx)); | ||
| return sourceFile.getFullText(); | ||
@@ -205,3 +166,3 @@ } | ||
| for (const op of getOperations) { | ||
| sourceFile.addVariableStatement(buildEnsureQueryDataFn(op, ctx)); | ||
| sourceFile.addVariableStatement(buildEnsureQueryDataFn(op)); | ||
| } | ||
@@ -208,0 +169,0 @@ return sourceFile.getFullText(); |
@@ -48,1 +48,7 @@ import { type ImportDeclarationStructure, Project } from "ts-morph"; | ||
| export declare function buildHookFileImports(ctx: GenerationContext): ImportDeclarationStructure[]; | ||
| /** | ||
| * Build all imports needed for the queryOptions file. | ||
| * Narrower than the hook file imports: queryOptions only needs the | ||
| * queryOptions/infiniteQueryOptions helpers, not the TanStack hooks. | ||
| */ | ||
| export declare function buildQueryOptionsFileImports(ctx: GenerationContext): ImportDeclarationStructure[]; |
@@ -56,2 +56,3 @@ import { IndentationText, NewLineKind, Project, QuoteKind, StructureKind, } from "ts-morph"; | ||
| { name: "FetchInfiniteQueryOptions", isTypeOnly: true }, | ||
| { name: "GetNextPageParamFunction", isTypeOnly: true }, | ||
| { name: "EnsureQueryDataOptions", isTypeOnly: true }, | ||
@@ -68,3 +69,7 @@ ], | ||
| moduleSpecifier: "@tanstack/react-query", | ||
| namedImports: [{ name: "queryOptions" }, { name: "infiniteQueryOptions" }], | ||
| namedImports: [ | ||
| { name: "queryOptions" }, | ||
| { name: "infiniteQueryOptions" }, | ||
| { name: "UseInfiniteQueryOptions", isTypeOnly: true }, | ||
| ], | ||
| }; | ||
@@ -142,1 +147,19 @@ } | ||
| } | ||
| /** | ||
| * Build all imports needed for the queryOptions file. | ||
| * Narrower than the hook file imports: queryOptions only needs the | ||
| * queryOptions/infiniteQueryOptions helpers, not the TanStack hooks. | ||
| */ | ||
| export function buildQueryOptionsFileImports(ctx) { | ||
| const imports = [ | ||
| buildCommonImport(), | ||
| buildQueryOptionsImport(), | ||
| buildClientImport(ctx), | ||
| buildServiceImport(ctx), | ||
| ]; | ||
| const modelImport = buildModelImport(ctx); | ||
| if (modelImport) { | ||
| imports.push(modelImport); | ||
| } | ||
| return imports; | ||
| } |
+14
-0
@@ -10,2 +10,16 @@ /** | ||
| capitalizedMethodName: string; | ||
| /** | ||
| * Generated Data type name read from the SDK function's own | ||
| * `Options<XData, ThrowOnError>` signature (e.g., "FindPetsData"). | ||
| * Not derivable from the method name: for digit-leading operationIds | ||
| * hey-api prefixes the function but strips the digits from the type (#213). | ||
| * Undefined when the SDK signature exposes no Data type. | ||
| * | ||
| * The rule this encodes: names hey-api owns (Data, Error) must be read from | ||
| * the SDK signature or share this field's stem; names this codegen mints | ||
| * itself (DefaultResponse, MutationResult, key fns) may be derived from | ||
| * capitalizedMethodName because they anchor back to the SDK via | ||
| * `typeof methodName`. | ||
| */ | ||
| dataTypeName?: string; | ||
| /** HTTP method (e.g., "GET", "POST", "PUT", "PATCH", "DELETE") */ | ||
@@ -12,0 +26,0 @@ httpMethod: string; |
+1
-1
| { | ||
| "name": "@7nohe/openapi-react-query-codegen", | ||
| "version": "3.0.1", | ||
| "version": "3.0.2", | ||
| "description": "OpenAPI React Query Codegen", | ||
@@ -5,0 +5,0 @@ "bin": { |
| import { type VariableStatementStructure } from "ts-morph"; | ||
| import type { GenerationContext, OperationInfo } from "../types.mjs"; | ||
| /** | ||
| * Build query key constant name (e.g., "findPetsQueryKey"). | ||
| */ | ||
| export declare function getQueryKeyName(op: OperationInfo): string; | ||
| /** | ||
| * Build mutation key constant name (e.g., "addPetMutationKey"). | ||
| */ | ||
| export declare function getMutationKeyName(op: OperationInfo): string; | ||
| /** | ||
| * Build query key fn name (e.g., "FindPetsQueryKeyFn"). | ||
| */ | ||
| export declare function getQueryKeyFnName(op: OperationInfo): string; | ||
| /** | ||
| * Build mutation key fn name (e.g., "AddPetMutationKeyFn"). | ||
| */ | ||
| export declare function getMutationKeyFnName(op: OperationInfo): string; | ||
| /** | ||
| * Build the query key constant export. | ||
| * Example: export const findPetsQueryKey = "FindPets"; | ||
| */ | ||
| export declare function buildQueryKeyExport(op: OperationInfo): VariableStatementStructure; | ||
| /** | ||
| * Build the mutation key constant export. | ||
| * Example: export const addPetMutationKey = "AddPet"; | ||
| */ | ||
| export declare function buildMutationKeyExport(op: OperationInfo): VariableStatementStructure; | ||
| /** | ||
| * Build the query key function export. | ||
| * Example: | ||
| * export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) => | ||
| * [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const; | ||
| */ | ||
| export declare function buildQueryKeyFnExport(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure; | ||
| /** | ||
| * Build the mutation key function export. | ||
| * Example: | ||
| * export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) => | ||
| * [addPetMutationKey, ...(mutationKey ?? [])] as const; | ||
| */ | ||
| export declare function buildMutationKeyFnExport(op: OperationInfo): VariableStatementStructure; |
| import { StructureKind, VariableDeclarationKind, } from "ts-morph"; | ||
| /** | ||
| * Build query key constant name (e.g., "findPetsQueryKey"). | ||
| */ | ||
| export function getQueryKeyName(op) { | ||
| return `${op.methodName}QueryKey`; | ||
| } | ||
| /** | ||
| * Build mutation key constant name (e.g., "addPetMutationKey"). | ||
| */ | ||
| export function getMutationKeyName(op) { | ||
| return `${op.methodName}MutationKey`; | ||
| } | ||
| /** | ||
| * Build query key fn name (e.g., "FindPetsQueryKeyFn"). | ||
| */ | ||
| export function getQueryKeyFnName(op) { | ||
| return `${op.capitalizedMethodName}QueryKeyFn`; | ||
| } | ||
| /** | ||
| * Build mutation key fn name (e.g., "AddPetMutationKeyFn"). | ||
| */ | ||
| export function getMutationKeyFnName(op) { | ||
| return `${op.capitalizedMethodName}MutationKeyFn`; | ||
| } | ||
| /** | ||
| * Build the query key constant export. | ||
| * Example: export const findPetsQueryKey = "FindPets"; | ||
| */ | ||
| export function buildQueryKeyExport(op) { | ||
| return { | ||
| kind: StructureKind.VariableStatement, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
| name: getQueryKeyName(op), | ||
| initializer: `"${op.capitalizedMethodName}"`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| /** | ||
| * Build the mutation key constant export. | ||
| * Example: export const addPetMutationKey = "AddPet"; | ||
| */ | ||
| export function buildMutationKeyExport(op) { | ||
| return { | ||
| kind: StructureKind.VariableStatement, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
| name: getMutationKeyName(op), | ||
| initializer: `"${op.capitalizedMethodName}"`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| /** | ||
| * Build the query key function export. | ||
| * Example: | ||
| * export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) => | ||
| * [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const; | ||
| */ | ||
| export function buildQueryKeyFnExport(op, ctx) { | ||
| const hasParams = op.parameters.length > 0; | ||
| const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`) | ||
| ? `${op.capitalizedMethodName}Data` | ||
| : "unknown"; | ||
| const params = []; | ||
| if (hasParams) { | ||
| const defaultValue = op.allParamsOptional ? " = {}" : ""; | ||
| params.push(`clientOptions: Options<${dataTypeName}, true>${defaultValue}`); | ||
| } | ||
| params.push("queryKey?: Array<unknown>"); | ||
| const fallbackArray = hasParams ? "[clientOptions]" : "[]"; | ||
| const body = `[${getQueryKeyName(op)}, ...(queryKey ?? ${fallbackArray})] as const`; | ||
| return { | ||
| kind: StructureKind.VariableStatement, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
| name: getQueryKeyFnName(op), | ||
| initializer: `(${params.join(", ")}) => ${body}`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| /** | ||
| * Build the mutation key function export. | ||
| * Example: | ||
| * export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) => | ||
| * [addPetMutationKey, ...(mutationKey ?? [])] as const; | ||
| */ | ||
| export function buildMutationKeyFnExport(op) { | ||
| const body = `[${getMutationKeyName(op)}, ...(mutationKey ?? [])] as const`; | ||
| return { | ||
| kind: StructureKind.VariableStatement, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
| name: getMutationKeyFnName(op), | ||
| initializer: `(mutationKey?: Array<unknown>) => ${body}`, | ||
| }, | ||
| ], | ||
| }; | ||
| } |
107525
0.5%1731
-4.89%