Skip to content

Commit

Permalink
refactor: split file generation and add suspense exports in own file
Browse files Browse the repository at this point in the history
  • Loading branch information
seriouslag committed Apr 2, 2024
1 parent e7117e1 commit 60fe3bd
Show file tree
Hide file tree
Showing 11 changed files with 873 additions and 321 deletions.
57 changes: 41 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
$ npm install -D @7nohe/openapi-react-query-codegen
```

Register the command to the `scripts` property in your package.json file.
Register the command to the `scripts` property in your package.json file.

```json
{
Expand All @@ -30,7 +30,6 @@ You can also run the command without installing it in your project using the npx
$ npx --package @7nohe/openapi-react-query-codegen openapi-rq -i ./petstore.yaml -c axios
```


## Usage

```
Expand Down Expand Up @@ -67,17 +66,18 @@ $ openapi-rq -i ./petstore.yaml
```
- openapi
- queries
- index.ts <- custom react hooks
- index.ts <- main file that exports common types, variables, and hooks
- common.ts <- common types
- queries.ts <- generated query hooks
- suspenses.ts <- generated suspense hooks
- requests <- output code generated by OpenAPI Typescript Codegen
```

### In your app

```tsx
// App.tsx
import {
usePetServiceFindPetsByStatus,
} from "../openapi/queries";
import { usePetServiceFindPetsByStatus } from "../openapi/queries";
function App() {
const { data } = usePetServiceFindPetsByStatus({ status: ["available"] });

Expand All @@ -100,30 +100,55 @@ You can also use pure TS clients.

```tsx
import { useQuery } from "@tanstack/react-query";
import { PetService } from '../openapi/requests/services/PetService';
import {
usePetServiceFindPetsByStatusKey,
} from "../openapi/queries";
import { PetService } from "../openapi/requests/services/PetService";
import { usePetServiceFindPetsByStatusKey } from "../openapi/queries";

function App() {
// You can still use the auto-generated query key
const { data } = useQuery({
queryKey: [usePetServiceFindPetsByStatusKey],
queryFn: () => {
// Do something here
return PetService.findPetsByStatus(['available']);
}
});
return PetService.findPetsByStatus(["available"]);
},
});

return <div className="App">{/* .... */}</div>;
}

export default App;
```

You can also use suspense hooks.

```tsx
// App.tsx
import { useDefaultClientFindPetsSuspense } from "../openapi/queries/suspense";
function ChildComponent() {
const { data } = useDefaultClientFindPetsSuspense({ tags: [], limit: 10 });

return (
<div className="App">
{/* .... */}
</div>
<ul>
{data?.map((pet, index) => (
<li key={pet.id}>{pet.name}</li>
))}
</ul>
);
}

function ParentComponent() {
return (
<>
<Suspense fallback={<>loading...</>}>
<ChildComponent />
</Suspense>
</>
);
}

export default App;
```

## License

MIT
8 changes: 7 additions & 1 deletion examples/react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "../openapi/queries";
import { useState } from "react";
import { queryClient } from "./queryClient";
import { SuspenseParent } from "./components/SuspenseParent";

function App() {
const [tags, _setTags] = useState<string[]>([]);
Expand All @@ -19,7 +20,8 @@ function App() {
// this defaults to any - here we are showing how to override the type
// Note - this is marked as deprecated in the OpenAPI spec and being passed to the client
const { data: notDefined } = useDefaultClientGetNotDefined<undefined>();
const { mutate: mutateNotDefined } = useDefaultClientPostNotDefined<undefined>();
const { mutate: mutateNotDefined } =
useDefaultClientPostNotDefined<undefined>();

const { mutate: addPet } = useDefaultClientAddPet();

Expand Down Expand Up @@ -60,6 +62,10 @@ function App() {
>
Create a pet
</button>
<div>
<h1>Suspense Components</h1>
<SuspenseParent />
</div>
</div>
);
}
Expand Down
17 changes: 17 additions & 0 deletions examples/react-app/src/components/SuspenseChild.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useDefaultClientFindPetsSuspense } from "../../openapi/queries/suspense";

export const SuspenseChild = () => {
const { data } = useDefaultClientFindPetsSuspense({ tags: [], limit: 10 });

if (!Array.isArray(data)) {
return <div>Error!</div>;
}

return (
<ul>
{data?.map((pet, index) => (
<li key={pet.id}>{pet.name}</li>
))}
</ul>
);
};
12 changes: 12 additions & 0 deletions examples/react-app/src/components/SuspenseParent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Suspense } from "react";
import { SuspenseChild } from "./SuspenseChild";

export const SuspenseParent = () => {
return (
<>
<Suspense fallback={<>loading...</>}>
<SuspenseChild />
</Suspense>
</>
);
};
49 changes: 49 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,56 @@
import { type PathLike } from "fs";
import { stat } from "fs/promises";
import ts, { JSDocComment, NodeArray, SourceFile } from "typescript";

export const TData = ts.factory.createIdentifier("TData");
export const TError = ts.factory.createIdentifier("TError");
export const TContext = ts.factory.createIdentifier("TContext");

export const queryKeyGenericType =
ts.factory.createTypeReferenceNode("TQueryKey");
export const queryKeyConstraint = ts.factory.createTypeReferenceNode("Array", [
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
]);

export const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};

export const lowercaseFirstLetter = (str: string) => {
return str.charAt(0).toLowerCase() + str.slice(1);
};

export const getNameFromMethod = (
method: ts.MethodDeclaration,
node: ts.SourceFile
) => {
return method.name.getText(node);
};

export type MethodDescription = {
className: string;
node: SourceFile;
method: ts.MethodDeclaration;
methodBlock: ts.Block;
httpMethodName: string;
jsDoc: (string | NodeArray<JSDocComment> | undefined)[];
isDeprecated: boolean;
};

export async function exists(f: PathLike) {
try {
await stat(f);
return true;
} catch {
return false;
}
}

const Common = "Common";

export function BuildCommonTypeName(name: string | ts.Identifier) {
if (typeof name === "string") {
return ts.factory.createIdentifier(`${Common}.${name}`);
}
return ts.factory.createIdentifier(`${Common}.${name.text}`);
}
Loading

0 comments on commit 60fe3bd

Please sign in to comment.