Skip to content

Commit 1335879

Browse files
Fix Query parameters reactivity in Vue (#931)
1 parent 80bb6ca commit 1335879

40 files changed

+2475
-565
lines changed

.github/workflows/tests.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ jobs:
1616
os: [ubuntu-latest, windows-latest]
1717
node-version: [18.x]
1818
steps:
19+
- name: Set git to use LF (instead of CRLF on Windows)
20+
run: |
21+
git config --global core.autocrlf false
22+
git config --global core.eol lf
1923
- uses: actions/checkout@v4
2024
- name: Use Node.js ${{ matrix.node-version }}
2125
uses: actions/setup-node@v4
@@ -25,6 +29,8 @@ jobs:
2529
run: yarn
2630
- name: build
2731
run: yarn build
32+
- name: formatting
33+
run: yarn format:check
2834
- name: unit tests
2935
run: yarn test:ci
3036
- name: tests install

.node-version

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
18

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
18

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ node_modules
55

66
# Build
77
.next
8+
**/dist

.prettierrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"tabWidth": 2,
66
"semi": true,
77
"singleQuote": true,
8-
"trailingComma": "all"
8+
"trailingComma": "all",
9+
"endOfLine": "lf"
910
}

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"private": true,
1010
"scripts": {
1111
"format": "prettier --write .",
12+
"format:check": "prettier --check .",
1213
"format:staged": "pretty-quick --staged",
1314
"prerelease": "yarn build && cd ./tests && yarn generate && yarn build",
1415
"release": "dotenv release-it",
@@ -18,7 +19,8 @@
1819
"update-samples": "zx ./scripts/update-samples.mjs",
1920
"build": "turbo run build",
2021
"test": "turbo run test",
21-
"test:ci": "turbo run test -- --run",
22+
"test:ci": "yarn test -- -- --run && yarn test:vue-query --run",
23+
"test:vue-query": "cd ./samples/vue-query && yarn && yarn test",
2224
"lint": "turbo run lint",
2325
"dev": "turbo run dev"
2426
},

packages/core/src/generators/options.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const generateAxiosOptions = ({
3737
headers,
3838
requestOptions,
3939
hasSignal,
40+
isVue,
4041
paramsSerializer,
4142
paramsSerializerOptions,
4243
}: {
@@ -46,6 +47,7 @@ export const generateAxiosOptions = ({
4647
headers?: GeneratorSchema;
4748
requestOptions?: object | boolean;
4849
hasSignal: boolean;
50+
isVue: boolean;
4951
paramsSerializer?: GeneratorMutator;
5052
paramsSerializerOptions?: ParamsSerializerOptions;
5153
}) => {
@@ -96,7 +98,11 @@ export const generateAxiosOptions = ({
9698
value += '\n ...options,';
9799

98100
if (queryParams) {
99-
value += '\n params: {...params, ...options?.params},';
101+
if (isVue) {
102+
value += '\n params: {...unref(params), ...options?.params},';
103+
} else {
104+
value += '\n params: {...params, ...options?.params},';
105+
}
100106
}
101107

102108
if (headers) {
@@ -130,6 +136,7 @@ export const generateOptions = ({
130136
isAngular,
131137
isExactOptionalPropertyTypes,
132138
hasSignal,
139+
isVue,
133140
paramsSerializer,
134141
paramsSerializerOptions,
135142
}: {
@@ -145,6 +152,7 @@ export const generateOptions = ({
145152
isAngular?: boolean;
146153
isExactOptionalPropertyTypes: boolean;
147154
hasSignal: boolean;
155+
isVue?: boolean;
148156
paramsSerializer?: GeneratorMutator;
149157
paramsSerializerOptions?: ParamsSerializerOptions;
150158
}) => {
@@ -160,6 +168,7 @@ export const generateOptions = ({
160168
requestOptions,
161169
isExactOptionalPropertyTypes,
162170
hasSignal,
171+
isVue: isVue ?? false,
163172
paramsSerializer,
164173
paramsSerializerOptions,
165174
});
@@ -207,6 +216,7 @@ export const generateBodyMutatorConfig = (
207216

208217
export const generateQueryParamsAxiosConfig = (
209218
response: GetterResponse,
219+
isVue: boolean,
210220
queryParams?: GetterQueryParam,
211221
) => {
212222
if (!queryParams && !response.isBlob) {
@@ -216,7 +226,11 @@ export const generateQueryParamsAxiosConfig = (
216226
let value = '';
217227

218228
if (queryParams) {
219-
value += ',\n params';
229+
if (isVue) {
230+
value += ',\n params: unref(params)';
231+
} else {
232+
value += ',\n params';
233+
}
220234
}
221235

222236
if (response.isBlob) {
@@ -238,6 +252,7 @@ export const generateMutatorConfig = ({
238252
isBodyVerb,
239253
hasSignal,
240254
isExactOptionalPropertyTypes,
255+
isVue,
241256
}: {
242257
route: string;
243258
body: GetterBody;
@@ -250,13 +265,15 @@ export const generateMutatorConfig = ({
250265
isBodyVerb: boolean;
251266
hasSignal: boolean;
252267
isExactOptionalPropertyTypes: boolean;
268+
isVue?: boolean;
253269
}) => {
254270
const bodyOptions = isBodyVerb
255271
? generateBodyMutatorConfig(body, isFormData, isFormUrlEncoded)
256272
: '';
257273

258274
const queryParamsOptions = generateQueryParamsAxiosConfig(
259275
response,
276+
isVue ?? false,
260277
queryParams,
261278
);
262279

packages/core/src/getters/discriminators.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SchemasObject } from 'openapi3-ts';
22
import { ContextSpecs } from '../types';
33
import { getRefInfo } from './ref';
4-
import { pascal } from "../utils";
4+
import { pascal } from '../utils';
55

66
export const resolveDiscriminators = (
77
schemas: SchemasObject,
@@ -19,7 +19,7 @@ export const resolveDiscriminators = (
1919
try {
2020
const { originalName } = getRefInfo(mappingValue, context);
2121
// name from getRefInfo may contain a suffix, which we don't want
22-
const name = pascal(originalName)
22+
const name = pascal(originalName);
2323
subTypeSchema = transformedSchemas[name];
2424
} catch (e) {
2525
subTypeSchema = transformedSchemas[mappingValue];

packages/core/src/getters/object.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ export const getObject = ({
181181
}
182182

183183
return {
184-
value: (item.type === 'object' ? '{ [key: string]: any }' : 'unknown') + nullable,
184+
value:
185+
(item.type === 'object' ? '{ [key: string]: any }' : 'unknown') +
186+
nullable,
185187
imports: [],
186188
schemas: [],
187189
isEnum: false,

packages/core/src/writers/schemas.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const writeSchemas = async ({
110110
if (indexFiles) {
111111
const schemaFilePath = upath.join(schemaPath, '/index.ts');
112112
await fs.ensureFile(schemaFilePath);
113-
113+
114114
// Ensure separate files are used for parallel schema writing.
115115
// Throw an exception, which list all duplicates, before attempting
116116
// multiple writes on the same file.
@@ -134,7 +134,7 @@ export const writeSchemas = async ({
134134
.join('\n'),
135135
);
136136
}
137-
137+
138138
try {
139139
const data = await fs.readFile(schemaFilePath);
140140

packages/query/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
"scripts": {
1111
"build": "tsup ./src/index.ts --target node12 --clean --dts",
1212
"dev": "tsup ./src/index.ts --target node12 --clean --watch src",
13-
"lint": "eslint src/**/*.ts"
13+
"lint": "eslint src/**/*.ts",
14+
"test": "vitest"
1415
},
1516
"dependencies": {
1617
"@orval/core": "6.21.0",
17-
"lodash.omitby": "^4.6.0"
18+
"lodash.omitby": "^4.6.0",
19+
"vitest": "^0.34.6"
1820
},
1921
"devDependencies": {
2022
"@types/lodash.omitby": "^4.6.7"

packages/query/src/index.test.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { it, expect, describe } from 'vitest';
2+
import { builder } from './index';
3+
import {
4+
GeneratorOptions,
5+
GeneratorVerbOptions,
6+
NormalizedOverrideOutput,
7+
} from '@orval/core';
8+
9+
describe('throws when trying to use named parameters with vue-query client', () => {
10+
it('vue-query builder type', () => {
11+
expect(() =>
12+
builder({ type: 'vue-query' })().client(
13+
{} as GeneratorVerbOptions,
14+
{
15+
override: { useNamedParameters: true } as NormalizedOverrideOutput,
16+
} as GeneratorOptions,
17+
'axios',
18+
),
19+
).toThrowErrorMatchingInlineSnapshot(
20+
'"vue-query client does not support named parameters, and had broken reactivity previously, please set useNamedParameters to false; See for context: https://github.com/anymaniax/orval/pull/931#issuecomment-1752355686"',
21+
);
22+
});
23+
it('vue-query output client', () => {
24+
expect(() =>
25+
builder()().client(
26+
{} as GeneratorVerbOptions,
27+
{
28+
override: { useNamedParameters: true } as NormalizedOverrideOutput,
29+
} as GeneratorOptions,
30+
'vue-query',
31+
),
32+
).toThrowErrorMatchingInlineSnapshot(
33+
'"vue-query client does not support named parameters, and had broken reactivity previously, please set useNamedParameters to false; See for context: https://github.com/anymaniax/orval/pull/931#issuecomment-1752355686"',
34+
);
35+
});
36+
});

packages/query/src/index.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
jsDoc,
3333
GetterQueryParam,
3434
compareVersions,
35+
getRouteAsArray,
3536
} from '@orval/core';
3637
import omitBy from 'lodash.omitby';
3738
import {
@@ -401,6 +402,7 @@ const generateQueryRequestFunction = (
401402
isBodyVerb,
402403
hasSignal,
403404
isExactOptionalPropertyTypes,
405+
isVue: isVue(outputClient),
404406
});
405407

406408
let bodyDefinition = body.definition.replace('[]', '\\[\\]');
@@ -468,6 +470,7 @@ const generateQueryRequestFunction = (
468470
paramsSerializerOptions: override?.paramsSerializerOptions,
469471
isExactOptionalPropertyTypes,
470472
hasSignal,
473+
isVue: isVue(outputClient),
471474
});
472475

473476
const optionsArgs = generateRequestOptionsArguments({
@@ -1208,10 +1211,12 @@ const generateQueryHook = async (
12081211
'implementation',
12091212
);
12101213

1211-
const routeString = `\`${route}\``;
1214+
const routeString = isVue(outputClient)
1215+
? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this
1216+
: `\`${route}\``;
12121217

1218+
// Note: do not unref() params in Vue - this will make key lose reactivity
12131219
const queryKeyFn = `export const ${queryKeyFnName} = (${queryKeyProps}) => {
1214-
${isVue(outputClient) ? vueUnRefParams(props) : ''}
12151220
return [${routeString}${queryParams ? ', ...(params ? [params]: [])' : ''}${
12161221
body.implementation ? `, ${body.implementation}` : ''
12171222
}] as const;
@@ -1496,6 +1501,15 @@ export const builder =
14961501
} = {}) =>
14971502
() => {
14981503
const client: ClientBuilder = (verbOptions, options, outputClient) => {
1504+
if (
1505+
options.override.useNamedParameters &&
1506+
(type === 'vue-query' || outputClient === 'vue-query')
1507+
) {
1508+
throw new Error(
1509+
`vue-query client does not support named parameters, and had broken reactivity previously, please set useNamedParameters to false; See for context: https://github.com/anymaniax/orval/pull/931#issuecomment-1752355686`,
1510+
);
1511+
}
1512+
14991513
if (queryOptions) {
15001514
const normalizedQueryOptions = normalizeQueryOptions(
15011515
queryOptions,
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setupWorker } from 'msw/browser';
2-
import { getSwaggerPetstoreMSW } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
2+
import { getSwaggerPetstoreMock } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
33

4-
const worker = setupWorker(...getSwaggerPetstoreMSW());
4+
const worker = setupWorker(...getSwaggerPetstoreMock());
55

66
worker.start();

samples/react-app/src/mock.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setupWorker } from 'msw/browser';
2-
import { getSwaggerPetstoreMSW } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
2+
import { getSwaggerPetstoreMock } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
33

4-
const worker = setupWorker(...getSwaggerPetstoreMSW());
4+
const worker = setupWorker(...getSwaggerPetstoreMock());
55

66
worker.start();

samples/react-query/basic/src/mock.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setupWorker } from 'msw/browser';
2-
import { getSwaggerPetstoreMSW } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
2+
import { getSwaggerPetstoreMock } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
33

4-
const worker = setupWorker(...getSwaggerPetstoreMSW());
4+
const worker = setupWorker(...getSwaggerPetstoreMock());
55

66
worker.start();
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setupWorker } from 'msw/browser';
2-
import { getSwaggerPetstoreMSW } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
2+
import { getSwaggerPetstoreMock } from './api/endpoints/petstoreFromFileSpecWithTransformer.msw';
33

4-
const worker = setupWorker(...getSwaggerPetstoreMSW());
4+
const worker = setupWorker(...getSwaggerPetstoreMock());
55

66
worker.start();

0 commit comments

Comments
 (0)