Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ jobs:
run: yarn
- name: build
run: yarn build
- name: lint
# not all packages pass linting yet - ongoing work
run: |
yarn workspace @orval/angular lint
yarn workspace @orval/axios lint
yarn workspace @orval/fetch lint
yarn workspace @orval/hono lint
yarn workspace @orval/mcp lint
yarn workspace @orval/swr lint
- name: samples up to date
uses: nickcharlton/[email protected]
with:
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{
"cSpell.words": [
"axios",
"Emptyish",
"hono",
"listitem",
"openapi",
"Orval",
"Petstore",
"tanstack"
"tanstack",
"zods"
],
"files.associations": {
"turbo.json": "jsonc"
Expand Down
19 changes: 19 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,28 @@ export default defineConfig(
'no-case-declarations': 'warn',
'no-prototype-builtins': 'warn',
'unicorn/prevent-abbreviations': 'off',
'unicorn/consistent-function-scoping': [
'error',
{ checkArrowFunctions: false },
],
'@typescript-eslint/restrict-template-expressions': [
'error',
// default (not strict) settings
// consider tightening these in the future
{
allow: [{ name: ['Error', 'URL', 'URLSearchParams'], from: 'lib' }],
allowAny: true,
allowBoolean: true,
allowNullish: true,
allowNumber: true,
allowRegExp: true,
},
],

// enable these in the future
'unicorn/no-null': 'warn',
'unicorn/prefer-at': 'off',
'unicorn/no-array-reduce': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
Expand Down
15 changes: 9 additions & 6 deletions packages/angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export const generateAngularFooter: ClientFooterBuilder = ({

for (const operationName of operationNames) {
if (returnTypesToWrite.has(operationName)) {
// Map.has ensures Map.get will not return undefined, but TS still complains
// bug https://github.com/microsoft/TypeScript/issues/13086
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
footer += returnTypesToWrite.get(operationName) + '\n';
}
}
Expand All @@ -142,9 +145,9 @@ const generateImplementation = (
}: GeneratorVerbOptions,
{ route, context }: GeneratorOptions,
) => {
const isRequestOptions = override?.requestOptions !== false;
const isFormData = !override?.formData.disabled;
const isFormUrlEncoded = override?.formUrlEncoded !== false;
const isRequestOptions = override.requestOptions !== false;
const isFormData = !override.formData.disabled;
const isFormUrlEncoded = override.formUrlEncoded !== false;
const isExactOptionalPropertyTypes =
!!context.output.tsconfig?.compilerOptions?.exactOptionalPropertyTypes;
const bodyForm = generateFormDataAndUrlEncodedFunction({
Expand Down Expand Up @@ -180,7 +183,7 @@ const generateImplementation = (

const requestOptions = isRequestOptions
? generateMutatorRequestOptions(
override?.requestOptions,
override.requestOptions,
mutator.hasThirdArg,
)
: '';
Expand Down Expand Up @@ -213,11 +216,11 @@ const generateImplementation = (
queryParams,
response,
verb,
requestOptions: override?.requestOptions,
requestOptions: override.requestOptions,
isFormData,
isFormUrlEncoded,
paramsSerializer,
paramsSerializerOptions: override?.paramsSerializerOptions,
paramsSerializerOptions: override.paramsSerializerOptions,
isAngular: true,
isExactOptionalPropertyTypes,
hasSignal: false,
Expand Down
20 changes: 10 additions & 10 deletions packages/axios/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ const generateAxiosImplementation = (
}: GeneratorVerbOptions,
{ route, context }: GeneratorOptions,
) => {
const isRequestOptions = override?.requestOptions !== false;
const isFormData = !override?.formData.disabled;
const isFormUrlEncoded = override?.formUrlEncoded !== false;
const isRequestOptions = override.requestOptions !== false;
const isFormData = !override.formData.disabled;
const isFormUrlEncoded = override.formUrlEncoded !== false;
const isExactOptionalPropertyTypes =
!!context.output.tsconfig?.compilerOptions?.exactOptionalPropertyTypes;

Expand Down Expand Up @@ -110,7 +110,7 @@ const generateAxiosImplementation = (

const requestOptions = isRequestOptions
? generateMutatorRequestOptions(
override?.requestOptions,
override.requestOptions,
mutator.hasSecondArg,
)
: '';
Expand Down Expand Up @@ -154,11 +154,11 @@ const generateAxiosImplementation = (
queryParams,
response,
verb,
requestOptions: override?.requestOptions,
requestOptions: override.requestOptions,
isFormData,
isFormUrlEncoded,
paramsSerializer,
paramsSerializerOptions: override?.paramsSerializerOptions,
paramsSerializerOptions: override.paramsSerializerOptions,
isExactOptionalPropertyTypes,
hasSignal: false,
});
Expand Down Expand Up @@ -222,6 +222,9 @@ export const generateAxiosFooter: ClientFooterBuilder = ({

for (const operationName of operationNames) {
if (returnTypesToWrite.has(operationName)) {
// Map.has ensures Map.get will not return undefined, but TS still complains
// bug https://github.com/microsoft/TypeScript/issues/13086
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const func = returnTypesToWrite.get(operationName)!;
footer += func(noFunction ? undefined : title) + '\n';
}
Expand All @@ -240,10 +243,7 @@ export const generateAxios = (
return { implementation, imports };
};

export const generateAxiosFunctions: ClientBuilder = async (
verbOptions,
options,
) => {
export const generateAxiosFunctions: ClientBuilder = (verbOptions, options) => {
const { implementation, imports } = generateAxios(verbOptions, options);

return {
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface NormalizedOptions {

export type NormalizedOutputOptions = {
workspace?: string;
target?: string;
target: string;
schemas?: string;
namingConvention: NamingConvention;
fileExtension: string;
Expand Down Expand Up @@ -71,8 +71,8 @@ export type NormalizedOverrideOutput = {
title?: (title: string) => string;
transformer?: OutputTransformer;
mutator?: NormalizedMutator;
operations: Record<string, NormalizedOperationOptions>;
tags: Record<string, NormalizedOperationOptions>;
operations: Record<string, NormalizedOperationOptions | undefined>;
tags: Record<string, NormalizedOperationOptions | undefined>;
mock?: OverrideMockOptions;
contentType?: OverrideOutputContentType;
header: false | ((info: InfoObject) => string[] | string);
Expand Down Expand Up @@ -208,7 +208,7 @@ export type EnumGeneration =

export type OutputOptions = {
workspace?: string;
target?: string;
target: string;
schemas?: string;
namingConvention?: NamingConvention;
fileExtension?: string;
Expand Down Expand Up @@ -546,7 +546,7 @@ export type NormalizedZodOptions = {
body: boolean | ZodCoerceType[];
response: boolean | ZodCoerceType[];
};
preprocess: {
preprocess?: {
param?: NormalizedMutator;
query?: NormalizedMutator;
header?: NormalizedMutator;
Expand Down Expand Up @@ -616,9 +616,9 @@ export type AngularOptions = {
export type SwrOptions = {
useInfinite?: boolean;
useSWRMutationForGet?: boolean;
swrOptions?: any;
swrMutationOptions?: any;
swrInfiniteOptions?: any;
swrOptions?: unknown;
swrMutationOptions?: unknown;
swrInfiniteOptions?: unknown;
};

export type NormalizedFetchOptions = {
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/utils/get-property-safe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Type safe way to get arbitrary property from an object.
*
* @param obj - The object from which to retrieve the property.
* @param propertyName - The name of the property to retrieve.
* @returns Object with `hasProperty: true` and `value` of the property if it exists; otherwise `hasProperty: false` and undefined.
*
* @remarks Until TypeScript adds type-narrowing for Object.hasOwn we have to use this workaround
*/
export function getPropertySafe<T extends object, K extends keyof T>(
obj: T,
propertyName: K | string,
):
| { hasProperty: true; value: T[K] }
| { hasProperty: false; value: undefined } {
if (Object.hasOwn(obj, propertyName)) {
// safe to cast here because of the above check
return { hasProperty: true, value: obj[propertyName as K] };
}

return { hasProperty: false, value: undefined };
}
1 change: 1 addition & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './dynamic-import';
export * from './extension';
export * from './file';
export * from './file-extensions';
export * from './get-property-safe';
export * from './is-body-verb';
export * from './logger';
export * from './merge-deep';
Expand Down
18 changes: 10 additions & 8 deletions packages/fetch/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export const generateRequestFunction = (
}: GeneratorVerbOptions,
{ route, context, pathRoute }: GeneratorOptions,
) => {
const isRequestOptions = override?.requestOptions !== false;
const isFormData = !override?.formData.disabled;
const isFormUrlEncoded = override?.formUrlEncoded !== false;
const isRequestOptions = override.requestOptions !== false;
const isFormData = !override.formData.disabled;
const isFormUrlEncoded = override.formUrlEncoded !== false;

const getUrlFnName = camel(`get-${operationName}-url`);
const getUrlFnProps = toObjectString(
Expand All @@ -58,7 +58,7 @@ export const generateRequestFunction = (
| PathItemObject
| undefined;
const parameters =
spec?.[verb]?.parameters || ([] as (ParameterObject | ReferenceObject)[]);
spec?.[verb]?.parameters ?? ([] as (ParameterObject | ReferenceObject)[]);

const explodeParameters = parameters.filter((parameter) => {
const { schema } = resolveRef<ParameterObject>(parameter, context);
Expand Down Expand Up @@ -129,9 +129,11 @@ ${
contentType === 'application/nd-json' ||
contentType === 'application/x-ndjson';

const isNdJson = response.contentTypes.some(isContentTypeNdJson);
const isNdJson = response.contentTypes.some((contentType) =>
isContentTypeNdJson(contentType),
);
const responseTypeName = fetchResponseTypeName(
override.fetch?.includeHttpResponseReturnType,
override.fetch.includeHttpResponseReturnType,
isNdJson ? 'Response' : response.definition.success,
operationName,
);
Expand Down Expand Up @@ -230,8 +232,8 @@ ${override.fetch.forceSuccessResponse && hasSuccess ? '' : `export type ${respon
? `Promise<${successName}>`
: `Promise<${responseTypeName}>`;

const globalFetchOptions = isObject(override?.requestOptions)
? `${stringify(override?.requestOptions)?.slice(1, -1)?.trim()}`
const globalFetchOptions = isObject(override.requestOptions)
? stringify(override.requestOptions)?.slice(1, -1).trim()
: '';
const fetchMethodOption = `method: '${verb.toUpperCase()}'`;
const ignoreContentTypes = ['multipart/form-data'];
Expand Down
Loading