Skip to content

Commit 78d413d

Browse files
authored
Merge branch 'reduxjs:master' into chore/upgrade-graphql-request
2 parents b745842 + d8190e3 commit 78d413d

25 files changed

+791
-449
lines changed

.github/workflows/test-codegen.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,4 @@ jobs:
147147
name: package
148148

149149
- name: Run are-the-types-wrong
150-
run: yarn dlx @arethetypeswrong/cli@latest ./package.tgz --format table
150+
run: yarn dlx @arethetypeswrong/cli@latest ./package.tgz --format table --exclude-entrypoints cli

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ typesversions
3131
.pnp.*
3232
*.tgz
3333

34-
tsconfig.vitest-temp.json
34+
tsconfig.vitest-temp.json
35+
36+
# node version manager files
37+
.node-version
38+
.nvmrc

docs/rtk-query/usage/code-generation.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ const api = await generateEndpoints({
8484
})
8585
```
8686

87+
#### With Node.js Child process
88+
89+
```ts no-transpile title="bin/openapi-codegen.ts"
90+
import { exec } from 'node:child_process'
91+
92+
const cliPath = require.resolve('@rtk-query/codegen-openapi/cli')
93+
94+
// you can also use esbuild-runner (esr) or ts-node instead of tsx
95+
exec(`tsx ${cliPath} config.ts`)
96+
```
97+
8798
### Config file options
8899

89100
#### Simple usage

packages/rtk-query-codegen-openapi/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"types": "./lib/index.d.ts",
1919
"default": "./lib/index.js"
2020
}
21-
}
21+
},
22+
"./cli": "./lib/bin/cli.mjs"
2223
},
2324
"repository": {
2425
"type": "git",
@@ -77,7 +78,7 @@
7778
"@apidevtools/swagger-parser": "^10.1.1",
7879
"commander": "^6.2.0",
7980
"lodash.camelcase": "^4.3.0",
80-
"oazapfts": "^6.1.0",
81+
"oazapfts": "^6.3.0",
8182
"prettier": "^3.2.5",
8283
"semver": "^7.3.5",
8384
"swagger2openapi": "^7.0.4",

packages/rtk-query-codegen-openapi/src/generate.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export async function generateApi(
117117
useEnumType = false,
118118
mergeReadWriteOnly = false,
119119
httpResolverOptions,
120+
useUnknown = false,
120121
}: GenerationOptions
121122
) {
122123
const v3Doc = (v3DocCache[spec] ??= await getV3Doc(spec, httpResolverOptions));
@@ -125,6 +126,7 @@ export async function generateApi(
125126
unionUndefined,
126127
useEnumType,
127128
mergeReadWriteOnly,
129+
useUnknown,
128130
});
129131

130132
// temporary workaround for https://github.com/oazapfts/oazapfts/issues/491
@@ -448,7 +450,11 @@ export async function generateApi(
448450
const encodedValue =
449451
encodeQueryParams && param.param?.in === 'query'
450452
? factory.createConditionalExpression(
451-
value,
453+
factory.createBinaryExpression(
454+
value,
455+
ts.SyntaxKind.ExclamationEqualsToken,
456+
factory.createNull()
457+
),
452458
undefined,
453459
factory.createCallExpression(factory.createIdentifier('encodeURIComponent'), undefined, [
454460
factory.createCallExpression(factory.createIdentifier('String'), undefined, [value]),

packages/rtk-query-codegen-openapi/src/types.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,79 +38,89 @@ export interface CommonOptions {
3838
*/
3939
schemaFile: string;
4040
/**
41-
* defaults to "api"
41+
* @default "api"
4242
*/
4343
apiImport?: string;
4444
/**
45-
* defaults to "enhancedApi"
45+
* @default "enhancedApi"
4646
*/
4747
exportName?: string;
4848
/**
49-
* defaults to "ApiArg"
49+
* @default "ApiArg"
5050
*/
5151
argSuffix?: string;
5252
/**
53-
* defaults to "ApiResponse"
53+
* @default "ApiResponse"
5454
*/
5555
responseSuffix?: string;
5656
/**
57-
* defaults to empty
57+
* @default ""
5858
*/
5959
operationNameSuffix?: string;
6060
/**
61-
* defaults to `false`
6261
* `true` will generate hooks for queries and mutations, but no lazyQueries
62+
* @default false
6363
*/
6464
hooks?: boolean | { queries: boolean; lazyQueries: boolean; mutations: boolean };
6565
/**
66-
* defaults to false
6766
* `true` will generate a union type for `undefined` properties like: `{ id?: string | undefined }` instead of `{ id?: string }`
67+
* @default false
6868
*/
6969
unionUndefined?: boolean;
7070
/**
71-
* defaults to false
7271
* `true` will result in all generated endpoints having `providesTags`/`invalidatesTags` declarations for the `tags` of their respective operation definition
72+
* @default false
7373
* @see https://redux-toolkit.js.org/rtk-query/usage/code-generation for more information
7474
*/
7575
tag?: boolean;
7676
/**
77-
* defaults to false
7877
* `true` will add `encodeURIComponent` to the generated path parameters
78+
* @default false
7979
*/
8080
encodePathParams?: boolean;
8181
/**
82-
* defaults to false
8382
* `true` will add `encodeURIComponent` to the generated query parameters
83+
* @default false
8484
*/
8585
encodeQueryParams?: boolean;
8686
/**
87-
* defaults to false
8887
* `true` will "flatten" the arg so that you can do things like `useGetEntityById(1)` instead of `useGetEntityById({ entityId: 1 })`
88+
* @default false
8989
*/
9090
flattenArg?: boolean;
9191
/**
92-
* default to false
9392
* If set to `true`, the default response type will be included in the generated code for all endpoints.
93+
* @default false
9494
* @see https://swagger.io/docs/specification/describing-responses/#default
9595
*/
9696
includeDefault?: boolean;
9797
/**
98-
* default to false
9998
* `true` will not generate separate types for read-only and write-only properties.
99+
* @default false
100100
*/
101101
mergeReadWriteOnly?: boolean;
102102
/**
103-
*
104103
* HTTPResolverOptions object that is passed to the SwaggerParser bundle function.
105104
*/
106105
httpResolverOptions?: SwaggerParser.HTTPResolverOptions;
107106

108107
/**
109-
* defaults to undefined
110108
* If present the given file will be used as prettier config when formatting the generated code. If undefined the default prettier config
111109
* resolution mechanism will be used.
110+
* @default undefined
112111
*/
113112
prettierConfigFile?: string;
113+
114+
/**
115+
* Determines the fallback type for empty schemas.
116+
*
117+
* If set to **`true`**, **`unknown`** will be used
118+
* instead of **`any`** when a schema is empty.
119+
*
120+
* @default false
121+
* @since 2.1.0
122+
*/
123+
useUnknown?: boolean;
114124
}
115125

116126
export type TextMatcher = string | RegExp | (string | RegExp)[];
@@ -128,8 +138,8 @@ export interface OutputFileOptions extends Partial<CommonOptions> {
128138
filterEndpoints?: EndpointMatcher;
129139
endpointOverrides?: EndpointOverrides[];
130140
/**
131-
* defaults to false
132141
* If passed as true it will generate TS enums instead of union of strings
142+
* @default false
133143
*/
134144
useEnumType?: boolean;
135145
}

packages/rtk-query-codegen-openapi/test/generateEndpoints.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ describe('option encodeQueryParams', () => {
242242
});
243243

244244
expect(api).toMatch(
245-
/params:\s*{\s*\n\s*status:\s*queryArg\.status\s*\?\s*encodeURIComponent\(\s*String\(queryArg\.status\)\s*\)\s*:\s*undefined\s*,?\s*\n\s*}/s
245+
/params:\s*{\s*\n\s*status:\s*queryArg\.status\s*!=\s*null\s*\?\s*encodeURIComponent\(\s*String\(queryArg\.status\)\s*\)\s*:\s*undefined\s*,?\s*\n\s*}/s
246246
);
247247
});
248248

packages/toolkit/src/createSlice.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ import type {
2323
ReducerWithInitialState,
2424
} from './createReducer'
2525
import { createReducer } from './createReducer'
26-
import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
26+
import type {
27+
ActionReducerMapBuilder,
28+
AsyncThunkReducers,
29+
TypedActionCreator,
30+
} from './mapBuilders'
2731
import { executeReducerBuilderCallback } from './mapBuilders'
2832
import type { Id, TypeGuard } from './tsHelpers'
2933
import { getOrInsertComputed } from './utils'
@@ -300,25 +304,7 @@ type AsyncThunkSliceReducerConfig<
300304
ThunkArg extends any,
301305
Returned = unknown,
302306
ThunkApiConfig extends AsyncThunkConfig = {},
303-
> = {
304-
pending?: CaseReducer<
305-
State,
306-
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
307-
>
308-
rejected?: CaseReducer<
309-
State,
310-
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
311-
>
312-
fulfilled?: CaseReducer<
313-
State,
314-
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
315-
>
316-
settled?: CaseReducer<
317-
State,
318-
ReturnType<
319-
AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
320-
>
321-
>
307+
> = AsyncThunkReducers<State, ThunkArg, Returned, ThunkApiConfig> & {
322308
options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
323309
}
324310

packages/toolkit/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export {
1010
original,
1111
isDraft,
1212
} from 'immer'
13-
export type { Draft } from 'immer'
13+
export type { Draft, WritableDraft } from 'immer'
1414
export {
1515
createSelector,
1616
createSelectorCreator,

packages/toolkit/src/mapBuilders.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@ import type {
55
ActionMatcherDescriptionCollection,
66
} from './createReducer'
77
import type { TypeGuard } from './tsHelpers'
8+
import type { AsyncThunk, AsyncThunkConfig } from './createAsyncThunk'
9+
10+
export type AsyncThunkReducers<
11+
State,
12+
ThunkArg extends any,
13+
Returned = unknown,
14+
ThunkApiConfig extends AsyncThunkConfig = {},
15+
> = {
16+
pending?: CaseReducer<
17+
State,
18+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
19+
>
20+
rejected?: CaseReducer<
21+
State,
22+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
23+
>
24+
fulfilled?: CaseReducer<
25+
State,
26+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
27+
>
28+
settled?: CaseReducer<
29+
State,
30+
ReturnType<
31+
AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
32+
>
33+
>
34+
}
835

936
export type TypedActionCreator<Type extends string> = {
1037
(...args: any[]): Action<Type>
@@ -31,7 +58,7 @@ export interface ActionReducerMapBuilder<State> {
3158
/**
3259
* Adds a case reducer to handle a single exact action type.
3360
* @remarks
34-
* All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
61+
* All calls to `builder.addCase` must come before any calls to `builder.addAsyncThunk`, `builder.addMatcher` or `builder.addDefaultCase`.
3562
* @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
3663
* @param reducer - The actual case reducer function.
3764
*/
@@ -40,12 +67,28 @@ export interface ActionReducerMapBuilder<State> {
4067
reducer: CaseReducer<State, A>,
4168
): ActionReducerMapBuilder<State>
4269

70+
/**
71+
* Adds case reducers to handle actions based on a `AsyncThunk` action creator.
72+
* @remarks
73+
* All calls to `builder.addAsyncThunk` must come before after any calls to `builder.addCase` and before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
74+
* @param asyncThunk - The async thunk action creator itself.
75+
* @param reducers - A mapping from each of the `AsyncThunk` action types to the case reducer that should handle those actions.
76+
*/
77+
addAsyncThunk<
78+
Returned,
79+
ThunkArg,
80+
ThunkApiConfig extends AsyncThunkConfig = {},
81+
>(
82+
asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
83+
reducers: AsyncThunkReducers<State, ThunkArg, Returned, ThunkApiConfig>,
84+
): Omit<ActionReducerMapBuilder<State>, 'addCase'>
85+
4386
/**
4487
* Allows you to match your incoming actions against your own filter function instead of only the `action.type` property.
4588
* @remarks
4689
* If multiple matcher reducers match, all of them will be executed in the order
4790
* they were defined in - even if a case reducer already matched.
48-
* All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
91+
* All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and `builder.addAsyncThunk` and before any calls to `builder.addDefaultCase`.
4992
* @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
5093
* function
5194
* @param reducer - The actual case reducer function.
@@ -99,7 +142,7 @@ const reducer = createReducer(initialState, (builder) => {
99142
addMatcher<A>(
100143
matcher: TypeGuard<A> | ((action: any) => boolean),
101144
reducer: CaseReducer<State, A extends Action ? A : A & Action>,
102-
): Omit<ActionReducerMapBuilder<State>, 'addCase'>
145+
): Omit<ActionReducerMapBuilder<State>, 'addCase' | 'addAsyncThunk'>
103146

104147
/**
105148
* Adds a "default case" reducer that is executed if no case reducer and no matcher
@@ -173,6 +216,35 @@ export function executeReducerBuilderCallback<S>(
173216
actionsMap[type] = reducer
174217
return builder
175218
},
219+
addAsyncThunk<
220+
Returned,
221+
ThunkArg,
222+
ThunkApiConfig extends AsyncThunkConfig = {},
223+
>(
224+
asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
225+
reducers: AsyncThunkReducers<S, ThunkArg, Returned, ThunkApiConfig>,
226+
) {
227+
if (process.env.NODE_ENV !== 'production') {
228+
// since this uses both action cases and matchers, we can't enforce the order in runtime other than checking for default case
229+
if (defaultCaseReducer) {
230+
throw new Error(
231+
'`builder.addAsyncThunk` should only be called before calling `builder.addDefaultCase`',
232+
)
233+
}
234+
}
235+
if (reducers.pending)
236+
actionsMap[asyncThunk.pending.type] = reducers.pending
237+
if (reducers.rejected)
238+
actionsMap[asyncThunk.rejected.type] = reducers.rejected
239+
if (reducers.fulfilled)
240+
actionsMap[asyncThunk.fulfilled.type] = reducers.fulfilled
241+
if (reducers.settled)
242+
actionMatchers.push({
243+
matcher: asyncThunk.settled,
244+
reducer: reducers.settled,
245+
})
246+
return builder
247+
},
176248
addMatcher<A>(
177249
matcher: TypeGuard<A>,
178250
reducer: CaseReducer<S, A extends Action ? A : A & Action>,

0 commit comments

Comments
 (0)