Skip to content

Commit 632a666

Browse files
authoredJan 13, 2024
feat: implement function generation using useSWRMutation (#1148)
* feat: add `swr/mutation` dependensies * feat: add conditional branching to implement verbs other than get * feat: implement function generation using `useSWRMutation` * feat: sepalate `httpFunction` and `swrMutationFetcherFunction` * feat: add type cast to `arg` * feat: enable query params * feat: use axios http function * feat: supported global mutator
1 parent dd5d406 commit 632a666

File tree

1 file changed

+292
-56
lines changed

1 file changed

+292
-56
lines changed
 

‎packages/swr/src/index.ts

+292-56
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const SWR_DEPENDENCIES: GeneratorDependency[] = [
6565
{ name: 'useSwr', values: true, default: true },
6666
{ name: 'SWRConfiguration' },
6767
{ name: 'Key' },
68+
{ name: 'Arguments' },
6869
],
6970
dependency: 'swr',
7071
},
@@ -81,6 +82,17 @@ const SWR_INFINITE_DEPENDENCIES: GeneratorDependency[] = [
8182
},
8283
];
8384

85+
const SWR_MUTATION_DEPENDENCIES: GeneratorDependency[] = [
86+
{
87+
exports: [
88+
{ name: 'useSWRMutation', values: true, default: true },
89+
{ name: 'SWRMutationConfiguration' },
90+
{ name: 'SWRMutationKey' },
91+
],
92+
dependency: 'swr/mutation',
93+
},
94+
];
95+
8496
export const getSwrDependencies: ClientDependenciesBuilder = (
8597
hasGlobalMutator: boolean,
8698
hasParamsSerializerOptions: boolean,
@@ -89,6 +101,7 @@ export const getSwrDependencies: ClientDependenciesBuilder = (
89101
...(hasParamsSerializerOptions ? PARAMS_SERIALIZER_DEPENDENCIES : []),
90102
...SWR_DEPENDENCIES,
91103
...SWR_INFINITE_DEPENDENCIES,
104+
...SWR_MUTATION_DEPENDENCIES,
92105
];
93106

94107
const generateSwrRequestFunction = (
@@ -231,6 +244,30 @@ const generateSwrArguments = ({
231244
} }\n`;
232245
};
233246

247+
const generateSwrMutationArguments = ({
248+
operationName,
249+
isRequestOptions,
250+
mutator,
251+
}: {
252+
operationName: string;
253+
isRequestOptions: boolean;
254+
mutator?: GeneratorMutator;
255+
}) => {
256+
const definition = `SWRMutationConfiguration<Awaited<ReturnType<typeof ${operationName}>>, TError, string, Arguments, Awaited<ReturnType<typeof ${operationName}>>> & { swrKey?: string }`;
257+
258+
if (!isRequestOptions) {
259+
return `swrOptions?: ${definition}`;
260+
}
261+
262+
return `options?: { swr?:${definition}, ${
263+
!mutator
264+
? `axios?: AxiosRequestConfig`
265+
: mutator?.hasSecondArg
266+
? `request?: SecondParameter<typeof ${mutator.name}>`
267+
: ''
268+
} }\n`;
269+
};
270+
234271
const generateSwrImplementation = ({
235272
operationName,
236273
swrKeyFnName,
@@ -398,6 +435,104 @@ ${doc}export const ${camel(`use-${operationName}`)} = <TError = ${errorType}>(
398435
return useSWRInfiniteImplementation + useSwrImplementation;
399436
};
400437

438+
const generateSwrMutationImplementation = ({
439+
isRequestOptions,
440+
operationName,
441+
swrKeyFnName,
442+
swrMutationFetcherName,
443+
swrKeyProperties,
444+
swrMutationFetcherProperties,
445+
swrProps,
446+
props,
447+
response,
448+
mutator,
449+
swrOptions,
450+
doc,
451+
}: {
452+
isRequestOptions: boolean;
453+
operationName: string;
454+
swrKeyFnName: string;
455+
swrMutationFetcherName: string;
456+
swrKeyProperties: string;
457+
swrMutationFetcherProperties: string;
458+
swrProps: string;
459+
props: GetterProps;
460+
response: GetterResponse;
461+
mutator?: GeneratorMutator;
462+
swrOptions: SwrOptions;
463+
doc?: string;
464+
}) => {
465+
const hasParamReservedWord = props.some(
466+
(prop: GetterProp) => prop.name === 'query',
467+
);
468+
const queryResultVarName = hasParamReservedWord ? '_query' : 'query';
469+
470+
const swrKeyImplementation = `const swrKey = swrOptions?.swrKey ?? ${swrKeyFnName}(${swrKeyProperties});`;
471+
472+
let errorType = `AxiosError<${response.definition.errors || 'unknown'}>`;
473+
474+
if (mutator) {
475+
errorType = mutator.hasErrorType
476+
? `ErrorType<${response.definition.errors || 'unknown'}>`
477+
: response.definition.errors || 'unknown';
478+
}
479+
480+
const useSwrImplementation = `
481+
export type ${pascal(
482+
operationName,
483+
)}MutationResult = NonNullable<Awaited<ReturnType<typeof ${operationName}>>>
484+
export type ${pascal(operationName)}MutationError = ${errorType}
485+
486+
${doc}export const ${camel(`use-${operationName}`)} = <TError = ${errorType}>(
487+
${swrProps} ${generateSwrMutationArguments({
488+
operationName,
489+
isRequestOptions,
490+
mutator,
491+
})}) => {
492+
493+
${
494+
isRequestOptions
495+
? `const {swr: swrOptions${
496+
!mutator
497+
? `, axios: axiosOptions`
498+
: mutator?.hasSecondArg
499+
? ', request: requestOptions'
500+
: ''
501+
}} = options ?? {}`
502+
: ''
503+
}
504+
505+
${swrKeyImplementation}
506+
const swrFn = ${swrMutationFetcherName}(${swrMutationFetcherProperties}${
507+
swrMutationFetcherProperties && isRequestOptions ? ',' : ''
508+
}${
509+
isRequestOptions
510+
? !mutator
511+
? `axiosOptions`
512+
: mutator?.hasSecondArg
513+
? 'requestOptions'
514+
: ''
515+
: ''
516+
});
517+
518+
const ${queryResultVarName} = useSWRMutation(swrKey, swrFn, ${
519+
swrOptions.options
520+
? `{
521+
${stringify(swrOptions.options)?.slice(1, -1)}
522+
...swrOptions
523+
}`
524+
: 'swrOptions'
525+
})
526+
527+
return {
528+
swrKey,
529+
...${queryResultVarName}
530+
}
531+
}\n`;
532+
533+
return useSwrImplementation;
534+
};
535+
401536
const generateSwrHook = (
402537
{
403538
queryParams,
@@ -415,74 +550,175 @@ const generateSwrHook = (
415550
{ route }: GeneratorOptions,
416551
) => {
417552
const isRequestOptions = override?.requestOptions !== false;
553+
const doc = jsDoc({ summary, deprecated });
418554

419-
if (verb !== Verbs.GET) {
420-
return '';
421-
}
555+
if (verb === Verbs.GET) {
556+
const swrKeyProperties = props
557+
.filter((prop) => prop.type !== GetterPropType.HEADER)
558+
.map((param) => {
559+
if (param.type === GetterPropType.NAMED_PATH_PARAMS)
560+
return param.destructured;
561+
return param.type === GetterPropType.BODY
562+
? body.implementation
563+
: param.name;
564+
})
565+
.join(',');
566+
567+
const swrProperties = props
568+
.map((param) => {
569+
if (param.type === GetterPropType.NAMED_PATH_PARAMS)
570+
return param.destructured;
571+
return param.type === GetterPropType.BODY
572+
? body.implementation
573+
: param.name;
574+
})
575+
.join(',');
422576

423-
const swrProperties = props
424-
.map((param) => {
425-
if (param.type === GetterPropType.NAMED_PATH_PARAMS)
426-
return param.destructured;
427-
return param.type === GetterPropType.BODY
428-
? body.implementation
429-
: param.name;
430-
})
431-
.join(',');
432-
433-
const swrKeyProperties = props
434-
.filter((prop) => prop.type !== GetterPropType.HEADER)
435-
.map((param) => {
436-
if (param.type === GetterPropType.NAMED_PATH_PARAMS)
437-
return param.destructured;
438-
return param.type === GetterPropType.BODY
439-
? body.implementation
440-
: param.name;
441-
})
442-
.join(',');
443-
444-
const swrKeyFnName = camel(`get-${operationName}-key`);
445-
const queryKeyProps = toObjectString(
446-
props.filter((prop) => prop.type !== GetterPropType.HEADER),
447-
'implementation',
448-
);
577+
const queryKeyProps = toObjectString(
578+
props.filter((prop) => prop.type !== GetterPropType.HEADER),
579+
'implementation',
580+
);
449581

450-
const swrKeyLoaderFnName = camel(`get-${operationName}-infinite-key-loader`);
451-
const swrKeyLoader = override.swr.useInfinite
452-
? `export const ${swrKeyLoaderFnName} = (${queryKeyProps}) => {
582+
const swrKeyFnName = camel(`get-${operationName}-key`);
583+
const swrKeyFn = `
584+
export const ${swrKeyFnName} = (${queryKeyProps}) => [\`${route}\`${
585+
queryParams ? ', ...(params ? [params]: [])' : ''
586+
}${body.implementation ? `, ${body.implementation}` : ''}] as const;
587+
\n`;
588+
589+
const swrKeyLoaderFnName = camel(
590+
`get-${operationName}-infinite-key-loader`,
591+
);
592+
const swrKeyLoader = override.swr.useInfinite
593+
? `export const ${swrKeyLoaderFnName} = (${queryKeyProps}) => {
453594
return (_: number, previousPageData: Awaited<ReturnType<typeof ${operationName}>>) => {
454595
if (previousPageData && !previousPageData.data) return null
455596
456597
return [\`${route}\`${queryParams ? ', ...(params ? [params]: [])' : ''}${
457-
body.implementation ? `, ${body.implementation}` : ''
458-
}] as const;
598+
body.implementation ? `, ${body.implementation}` : ''
599+
}] as const;
459600
}
460601
}\n`
461-
: '';
602+
: '';
462603

463-
const doc = jsDoc({ summary, deprecated });
604+
const swrImplementation = generateSwrImplementation({
605+
operationName,
606+
swrKeyFnName,
607+
swrKeyLoaderFnName,
608+
swrProperties,
609+
swrKeyProperties,
610+
params,
611+
props,
612+
mutator,
613+
isRequestOptions,
614+
response,
615+
swrOptions: override.swr,
616+
doc,
617+
});
464618

465-
const swrKeyFn = `
466-
export const ${swrKeyFnName} = (${queryKeyProps}) => [\`${route}\`${
467-
queryParams ? ', ...(params ? [params]: [])' : ''
468-
}${body.implementation ? `, ${body.implementation}` : ''}] as const;
469-
\n`;
619+
return swrKeyFn + swrKeyLoader + swrImplementation;
620+
} else {
621+
const queryKeyProps = toObjectString(
622+
props.filter(
623+
(prop) =>
624+
prop.type === GetterPropType.PARAM ||
625+
prop.type === GetterPropType.NAMED_PATH_PARAMS,
626+
),
627+
'implementation',
628+
);
629+
630+
const swrProps = toObjectString(
631+
props.filter(
632+
(prop) =>
633+
prop.type === GetterPropType.PARAM ||
634+
prop.type === GetterPropType.QUERY_PARAM ||
635+
prop.type === GetterPropType.NAMED_PATH_PARAMS,
636+
),
637+
'implementation',
638+
);
639+
640+
const swrMutationFetcherProperties = props
641+
.filter(
642+
(prop) =>
643+
prop.type === GetterPropType.PARAM ||
644+
prop.type === GetterPropType.QUERY_PARAM ||
645+
prop.type === GetterPropType.NAMED_PATH_PARAMS,
646+
)
647+
.map((param) => {
648+
if (param.type === GetterPropType.NAMED_PATH_PARAMS) {
649+
return param.destructured;
650+
} else {
651+
return param.name;
652+
}
653+
})
654+
.join(',');
655+
656+
const httpFnProperties = props
657+
.filter((prop) => prop.type !== GetterPropType.HEADER)
658+
.map((prop) => {
659+
if (prop.type === GetterPropType.NAMED_PATH_PARAMS) {
660+
return prop.destructured;
661+
} else if (prop.type === GetterPropType.BODY) {
662+
return `arg as ${prop.implementation.split(': ')[1]}`;
663+
} else {
664+
return prop.name;
665+
}
666+
})
667+
.join(', ');
668+
669+
const swrKeyProperties = props
670+
.filter(
671+
(prop) =>
672+
prop.type === GetterPropType.PARAM ||
673+
prop.type === GetterPropType.NAMED_PATH_PARAMS,
674+
)
675+
.map((prop) => {
676+
if (prop.type === GetterPropType.NAMED_PATH_PARAMS) {
677+
return prop.destructured;
678+
} else {
679+
return prop.name;
680+
}
681+
})
682+
.join(',');
683+
684+
const swrKeyFnName = camel(`get-${operationName}-mutation-key`);
685+
const swrMutationKeyFn = `export const ${swrKeyFnName} = (${queryKeyProps}) => \`${route}\` as const;\n`;
686+
687+
const swrMutationFetcherName = camel(
688+
`get-${operationName}-mutation-fetcher`,
689+
);
690+
const swrMutationFetcherType = mutator
691+
? `Promise<${response.definition.success || 'unknown'}>`
692+
: `Promise<AxiosResponse<${response.definition.success || 'unknown'}>>`;
693+
const swrMutationFetcherOptions =
694+
!mutator && isRequestOptions ? 'options?: AxiosRequestConfig' : '';
695+
696+
const swrMutationFetcherFn = `
697+
export const ${swrMutationFetcherName} = (${swrProps} ${swrMutationFetcherOptions}) => {
698+
return (_: string, { arg }: { arg: Arguments }): ${swrMutationFetcherType} => {
699+
return ${operationName}(${httpFnProperties}${
700+
swrMutationFetcherOptions.length ? ', options' : ''
701+
});
702+
}
703+
}\n`;
470704

471-
const swrImplementation = generateSwrImplementation({
472-
operationName,
473-
swrKeyFnName,
474-
swrKeyLoaderFnName,
475-
swrProperties,
476-
swrKeyProperties,
477-
params,
478-
props,
479-
mutator,
480-
isRequestOptions,
481-
response,
482-
swrOptions: override.swr,
483-
doc,
484-
});
485-
return swrKeyFn + swrKeyLoader + swrImplementation;
705+
const swrImplementation = generateSwrMutationImplementation({
706+
operationName,
707+
swrKeyFnName,
708+
swrMutationFetcherName,
709+
swrKeyProperties,
710+
swrMutationFetcherProperties,
711+
swrProps,
712+
props,
713+
isRequestOptions,
714+
response,
715+
mutator,
716+
swrOptions: override.swr,
717+
doc,
718+
});
719+
720+
return swrMutationFetcherFn + swrMutationKeyFn + swrImplementation;
721+
}
486722
};
487723

488724
export const generateSwrHeader: ClientHeaderBuilder = ({

0 commit comments

Comments
 (0)
Please sign in to comment.