Skip to content

Commit 506d5e7

Browse files
authored
Merge pull request #396 from OpenAPI-Qraft/feat/context-support
feat: Add Context-based API Client support for React Compiler compatibility
2 parents 0d9e540 + b775acf commit 506d5e7

51 files changed

Lines changed: 3253 additions & 566 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/ripe-parks-notice.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@openapi-qraft/tanstack-query-react-plugin': minor
3+
'@openapi-qraft/react': minor
4+
---
5+
6+
Add `context:` option to `--create-api-client-fn` for React Context-based API clients. Enables creating API clients outside components with static hooks, making them compatible with React Compiler optimization.

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ jobs:
2323
uses: actions/setup-node@v4
2424
with:
2525
cache: 'yarn'
26-
node-version: 20.19.6
26+
node-version: 22.22.0
2727

2828
- name: Install Dependencies
2929
run: yarn install --immutable
3030

31+
- name: Generate Mocks
32+
run: yarn workspace playground run generate:mocks
33+
3134
- name: Build
3235
run: yarn build
3336

.github/workflows/deploy-gh-pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
uses: actions/setup-node@v4
2828
with:
2929
cache: 'yarn'
30-
node-version: 20.19.6
30+
node-version: 22.22.0
3131

3232
- name: Install dependencies
3333
run: yarn install --immutable

.github/workflows/docs-validation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
uses: actions/setup-node@v4
2222
with:
2323
cache: 'yarn'
24-
node-version: 20.19.6
24+
node-version: 22.22.0
2525

2626
- name: Install dependencies
2727
run: yarn install --immutable

.github/workflows/e2e.yml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,24 @@ jobs:
2222
with:
2323
fetch-depth: 0
2424

25+
- name: Copy Playground for E2E Tests
26+
run: |
27+
mkdir -p "$TEST_PROJECTS_DIR"
28+
cp -a e2e/projects/. "$TEST_PROJECTS_DIR/"
29+
env:
30+
TEST_PROJECTS_DIR: "${{ runner.temp }}/projects"
31+
2532
- name: Setup Node.js 20.x
2633
uses: actions/setup-node@v4
2734
with:
2835
cache: 'yarn'
29-
node-version: 20.19.6
36+
node-version: 22.22.0
3037

3138
- name: Install Dependencies
3239
run: yarn install --immutable
3340

3441
- name: Build
35-
run: yarn build --filter "@openapi-qraft/*" --filter "@qraft/*"
36-
37-
- name: Copy Playground for E2E Tests
38-
run: |
39-
mkdir -p "$TEST_PROJECTS_DIR"
40-
cp -r playground "$TEST_PROJECTS_DIR"
41-
cp -a e2e/projects/. "$TEST_PROJECTS_DIR/"
42-
env:
43-
TEST_PROJECTS_DIR: "${{ runner.temp }}/projects"
42+
run: yarn build --filter "@openapi-qraft/*" --filter "@qraft/*" --force
4443

4544
- name: Remove Verdaccio Storage
4645
run: rm -rf e2e/verdaccio-storage
@@ -52,4 +51,12 @@ jobs:
5251
run: TEST_PROJECTS_DIR=${{ runner.temp }}/projects yarn run "e2e:update-projects-from-private-registry"
5352

5453
- name: Build E2E Projects
55-
run: TEST_PROJECTS_DIR=${{ runner.temp }}/projects yarn run "e2e:build-projects"
54+
run: TEST_PROJECTS_DIR=${{ runner.temp }}/projects yarn run "e2e:build-projects"
55+
56+
- name: Upload E2E Project API Clients
57+
uses: actions/upload-artifact@v4
58+
if: ${{ always() }}
59+
with:
60+
name: e2e-projects-api-clients
61+
path: |
62+
${{ runner.temp }}/projects/**/src

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ jobs:
2424
uses: actions/setup-node@v4
2525
with:
2626
cache: 'yarn'
27-
node-version: 20.19.6
27+
node-version: 22.22.0
2828

2929
- name: Install Dependencies
3030
run: yarn install --immutable
3131

3232
- name: Build
33-
run: yarn build
33+
run: yarn build --filter "@openapi-qraft/*" --force
3434

3535
- name: Create dummy npmrc # Prevent creation of '.npmrc' by 'changesets-gitlab' with the 'NPM_TOKEN'
3636
run: touch ".npmrc"

.github/workflows/versioning.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
uses: actions/setup-node@v4
2121
with:
2222
cache: 'yarn'
23-
node-version: 20.19.6
23+
node-version: 22.22.0
2424

2525
- name: Install Dependencies
2626
run: yarn install --immutable

packages/react-client/redocly.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,20 @@ apis:
7373
filename: create-internal-api-client
7474
services: all
7575
callbacks: all
76+
createMinimalAPIClient:
77+
services: none
78+
callbacks: none
79+
createNoCallbacksAPIClient:
80+
filename: createNoCallbacksAPIClient
81+
services: all
82+
callbacks: none
7683
createInternalReactAPIClient:
7784
services: none
85+
context: InternalReactAPIClientContext
7886
callbacks:
7987
[
88+
'useQuery',
89+
'useMutation',
8090
'setQueryData',
8191
'getQueryData',
8292
'getQueryKey',

packages/react-client/src/callbacks/useInfiniteQuery.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@ export const useInfiniteQuery: <
2929
>['useInfiniteQuery']
3030
>
3131
) => UseInfiniteQueryResult<TData, TError> = (qraftOptions, schema, args) => {
32-
return useInfiniteQueryBase(
32+
// @ts-expect-error - Too complex to type...
33+
const [queryOptions, queryClient] = useComposeUseQueryOptions(
34+
qraftOptions,
35+
schema,
3336
// @ts-expect-error - Too complex to type...
34-
...useComposeUseQueryOptions(qraftOptions, schema, args, true)
35-
) as never;
37+
args,
38+
true
39+
);
40+
41+
return useInfiniteQueryBase(queryOptions, queryClient) as never;
3642
};

packages/react-client/src/callbacks/useMutation.ts

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import type {
44
OperationSchema,
55
ServiceOperationMutationKey,
6-
ServiceOperationUseMutation,
6+
ServiceOperationUseMutationOptions,
77
} from '@openapi-qraft/tanstack-query-react-types';
88
import type { DefaultError, UseMutationResult } from '@tanstack/react-query';
99
import type { CreateAPIQueryClientOptions } from '../qraftAPIClient.js';
1010
import { useMutation as useMutationBase } from '@tanstack/react-query';
11+
import { useMemo } from 'react';
1112
import { composeMutationKey } from '../lib/composeMutationKey.js';
1213
import { requestFnResponseRejecter } from '../lib/requestFnResponseRejecter.js';
1314
import { requestFnResponseResolver } from '../lib/requestFnResponseResolver.js';
@@ -20,68 +21,91 @@ export const useMutation: <
2021
>(
2122
qraftOptions: CreateAPIQueryClientOptions,
2223
schema: OperationSchema,
23-
args: Parameters<
24-
ServiceOperationUseMutation<
25-
OperationSchema,
26-
object | undefined,
27-
TVariables,
28-
TData,
29-
DefaultError
30-
>['useMutation']
31-
>
24+
args: [
25+
parameters?: unknown,
26+
options?: ServiceOperationUseMutationOptions<any, any, any, any, any>,
27+
]
3228
) => UseMutationResult<TData, TError, TVariables, TContext> = (
3329
qraftOptions,
3430
schema,
3531
args
3632
) => {
3733
const [parameters, options] = args;
3834

39-
if (
40-
parameters &&
41-
typeof parameters === 'object' &&
42-
options &&
43-
'mutationKey' in options
44-
)
45-
throw new Error(
46-
`'useMutation': parameters and 'options.mutationKey' cannot be used together`
47-
);
48-
49-
const mutationKey =
35+
const optionsMutationKey =
5036
options && 'mutationKey' in options
5137
? (options.mutationKey as ServiceOperationMutationKey<
5238
typeof schema,
5339
unknown
5440
>)
55-
: composeMutationKey(schema, parameters);
41+
: undefined;
42+
43+
if (parameters && typeof parameters === 'object' && optionsMutationKey)
44+
throw new Error(
45+
`'useMutation': parameters and 'options.mutationKey' cannot be used together`
46+
);
47+
48+
const mutationKey = useMemo(
49+
() => optionsMutationKey ?? composeMutationKey(schema, parameters),
50+
[optionsMutationKey, schema, parameters]
51+
);
5652

5753
return useMutationBase(
5854
{
5955
...options,
6056
mutationKey,
61-
mutationFn:
62-
options?.mutationFn ??
63-
(parameters
64-
? function (bodyPayload) {
65-
return qraftOptions
66-
.requestFn(schema, {
67-
parameters,
68-
baseUrl: qraftOptions.baseUrl,
69-
body: bodyPayload as never,
70-
})
71-
.then(requestFnResponseResolver, requestFnResponseRejecter);
72-
}
73-
: function (parametersAndBodyPayload) {
74-
const { body, ...parameters } = parametersAndBodyPayload ?? {};
75-
76-
return qraftOptions
77-
.requestFn(schema, {
78-
parameters,
79-
baseUrl: qraftOptions.baseUrl,
80-
body,
81-
} as never)
82-
.then(requestFnResponseResolver, requestFnResponseRejecter);
83-
}),
57+
mutationFn: useMemo(
58+
() =>
59+
options?.mutationFn ??
60+
qraftMutationFn.bind(
61+
null,
62+
qraftOptions.requestFn,
63+
qraftOptions.baseUrl,
64+
schema,
65+
parameters
66+
),
67+
[
68+
options?.mutationFn,
69+
qraftOptions.requestFn,
70+
qraftOptions.baseUrl,
71+
schema,
72+
parameters,
73+
]
74+
),
8475
},
8576
qraftOptions.queryClient
8677
) as never;
8778
};
79+
80+
function qraftMutationFn(
81+
requestFn: CreateAPIQueryClientOptions['requestFn'],
82+
baseUrl: CreateAPIQueryClientOptions['baseUrl'],
83+
schema: OperationSchema,
84+
parameters: unknown,
85+
mutationVariables:
86+
| {
87+
body: unknown;
88+
}
89+
| undefined
90+
) {
91+
if (parameters) {
92+
return requestFn(schema, {
93+
parameters,
94+
baseUrl,
95+
body: mutationVariables as never,
96+
}).then(requestFnResponseResolver, requestFnResponseRejecter);
97+
}
98+
99+
const mutationParameters =
100+
typeof mutationVariables === 'object' && mutationVariables !== null
101+
? { ...mutationVariables }
102+
: undefined;
103+
const body = mutationParameters?.body;
104+
delete mutationParameters?.body;
105+
106+
return requestFn(schema, {
107+
parameters: mutationParameters,
108+
baseUrl,
109+
body,
110+
} as never).then(requestFnResponseResolver, requestFnResponseRejecter);
111+
}

0 commit comments

Comments
 (0)