Skip to content
This repository was archived by the owner on Nov 12, 2024. It is now read-only.

Commit 5fbda5a

Browse files
committed
Repair broken openapi-fetch types
1 parent 5a8ce45 commit 5fbda5a

File tree

14 files changed

+668
-194
lines changed

14 files changed

+668
-194
lines changed

.husky/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
lint-staged
2-
vitest --run --changed
2+
vitest --run --changed --typecheck

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ Wrapper hooks are provided 1:1 for each hook exported by SWR.
6868
```ts
6969
import createClient from "openapi-fetch";
7070

71-
import { createQueryHook } from "swr-openapi";
72-
import { createImmutableHook } from "swr-openapi/immutable";
73-
import { createInfiniteHook } from "swr-openapi/infinite";
74-
import { createMutateHook } from "swr-openapi/mutate";
71+
import {
72+
createQueryHook,
73+
createImmutableHook,
74+
createInfiniteHook,
75+
createMutateHook,
76+
} from "swr-openapi";
7577

7678
import { paths as SomeApiPaths } from "./some-api";
7779

eslint.config.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import eslint from "@eslint/js";
22
import tseslint from "typescript-eslint";
3+
import vitest from "@vitest/eslint-plugin";
34
import prettier from "eslint-config-prettier";
45

56
export default tseslint.config(
@@ -14,7 +15,6 @@ export default tseslint.config(
1415
},
1516
},
1617
},
17-
prettier,
1818
{
1919
files: ["**/*.{js,ts}"],
2020
rules: {
@@ -33,4 +33,31 @@ export default tseslint.config(
3333
"@typescript-eslint/only-throw-error": "off",
3434
},
3535
},
36+
{
37+
files: ["**/__test__/**"],
38+
plugins: {
39+
vitest,
40+
},
41+
settings: {
42+
vitest: {
43+
typecheck: true,
44+
},
45+
},
46+
languageOptions: {
47+
globals: {
48+
...vitest.environments.env.globals,
49+
},
50+
},
51+
rules: {
52+
...vitest.configs.all.rules,
53+
"vitest/no-hooks": "off",
54+
"vitest/padding-around-expect-groups": "off",
55+
"vitest/prefer-lowercase-title": "off",
56+
"vitest/prefer-expect-assertions": "off",
57+
"vitest/prefer-to-be-falsy": "off",
58+
"vitest/prefer-to-be-truthy": "off",
59+
"vitest/require-hook": "off",
60+
},
61+
},
62+
prettier,
3663
);

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"prepare": "husky",
3939
"prepublishOnly": "npm run build && npm run exports:check && npm run types:check && npm run format:check && npm test",
4040
"build": "del ./dist && tsc --project tsconfig.build.json",
41-
"test": "vitest run",
42-
"dev": "vitest",
41+
"test": "vitest run --typecheck",
42+
"dev": "vitest --typecheck",
4343
"lint": "eslint './src/**/*'",
4444
"types:check": "tsc --noEmit",
4545
"format": "prettier --write .",
@@ -57,6 +57,7 @@
5757
"@types/eslint-config-prettier": "6.11.3",
5858
"@types/lodash": "4.17.7",
5959
"@types/react": "18.3.5",
60+
"@vitest/eslint-plugin": "1.1.0",
6061
"del-cli": "5.1.0",
6162
"eslint": "9.10.0",
6263
"eslint-config-prettier": "9.1.0",

src/__test__/fixtures/petstore.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,13 @@ export interface operations {
495495
/** @description Status values that need to be considered for filter */
496496
status?: "available" | "pending" | "sold";
497497
};
498-
header?: never;
498+
header?: {
499+
"X-Example": string;
500+
};
499501
path?: never;
500-
cookie?: never;
502+
cookie?: {
503+
"some-cookie-key": string;
504+
};
501505
};
502506
requestBody?: never;
503507
responses: {
@@ -516,15 +520,19 @@ export interface operations {
516520
headers: {
517521
[name: string]: unknown;
518522
};
519-
content?: never;
523+
content: {
524+
"application/json": {
525+
message: "Invalid status";
526+
}
527+
};
520528
};
521529
};
522530
};
523531
findPetsByTags: {
524532
parameters: {
525-
query?: {
533+
query: {
526534
/** @description Tags to filter by */
527-
tags?: string[];
535+
tags: string[];
528536
};
529537
header?: never;
530538
path?: never;
@@ -578,14 +586,18 @@ export interface operations {
578586
headers: {
579587
[name: string]: unknown;
580588
};
581-
content?: never;
589+
content: {
590+
"application/json": {
591+
message: "Invalid pet configuration";
592+
};
593+
};
582594
};
583595
/** @description Pet not found */
584596
404: {
585597
headers: {
586598
[name: string]: unknown;
587599
};
588-
content?: never;
600+
content?: never
589601
};
590602
};
591603
};

src/__test__/infinite.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ describe("createInfiniteHook", () => {
2828

2929
// Invokes `useSWRInfinite`
3030
expect(useSWRInfinite).toHaveBeenCalledTimes(1);
31+
3132
let getKey = useSWRInfinite.mock.lastCall![0];
3233

3334
// Calling `getKey` should invoke `getInit`
3435
const key = getKey(0, null);
36+
3537
expect(getInit).toHaveBeenCalledTimes(1);
3638

3739
// `getInit` should be called with key loader arguments
@@ -44,6 +46,7 @@ describe("createInfiniteHook", () => {
4446
getInit.mockReturnValueOnce(null);
4547
useInfinite("/pet/{petId}", getInit);
4648
getKey = useSWRInfinite.mock.lastCall![0];
49+
4750
expect(getKey(0, null)).toBeNull();
4851
});
4952

@@ -70,18 +73,20 @@ describe("createInfiniteHook", () => {
7073

7174
await expect(() =>
7275
fetcher!(["some-key", "any-path", { some: "init" }]),
73-
).rejects.toThrowError(new Error("Yikes"));
76+
).rejects.toThrow(new Error("Yikes"));
7477
});
7578

7679
it("passes correct config to useSWRInfinite", () => {
7780
useInfinite("/pet/{petId}", vi.fn(), { initialSize: 5000 });
81+
7882
expect(useSWRInfinite).toHaveBeenLastCalledWith(
7983
expect.any(Function),
8084
expect.any(Function),
8185
{ initialSize: 5000 },
8286
);
8387

8488
useInfinite("/pet/{petId}", vi.fn());
89+
8590
expect(useSWRInfinite).toHaveBeenLastCalledWith(
8691
expect.any(Function),
8792
expect.any(Function),

src/__test__/mutate.test.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import { isMatch } from "lodash";
12
import createClient from "openapi-fetch";
23
import * as React from "react";
34
import * as SWR from "swr";
45
import type { ScopedMutator } from "swr/_internal";
56
import { afterEach, describe, expect, it, vi } from "vitest";
67
import { createMutateHook } from "../mutate.js";
78
import type { paths } from "./fixtures/petstore.js";
8-
import { isMatch } from "lodash";
99

1010
// Mock `useCallback` (return given function as-is)
1111
vi.mock("react");
@@ -44,10 +44,7 @@ describe("createMutateHook", () => {
4444
it("returns callback that invokes swr `mutate` with fn, data and options", async () => {
4545
expect(swrMutate).not.toHaveBeenCalled();
4646

47-
const data = {
48-
name: "doggie",
49-
photoUrls: ["https://example.com"],
50-
};
47+
const data = [{ name: "doggie", photoUrls: ["https://example.com"] }];
5148
const config = { throwOnError: false };
5249

5350
await mutate(["/pet/findByStatus"], data, config);
@@ -65,7 +62,7 @@ describe("createMutateHook", () => {
6562

6663
describe("useMutate -> mutate -> key matcher", () => {
6764
it("returns false for non-array keys", async () => {
68-
await mutate(["/pet/findByTags"]);
65+
await mutate(["/pet/findByStatus"]);
6966
const keyMatcher = getKeyMatcher();
7067

7168
expect(keyMatcher(null)).toBe(false);
@@ -75,7 +72,7 @@ describe("createMutateHook", () => {
7572
});
7673

7774
it("returns false for arrays with length !== 3", async () => {
78-
await mutate(["/pet/findByTags"]);
75+
await mutate(["/pet/findByStatus"]);
7976
const keyMatcher = getKeyMatcher();
8077

8178
expect(keyMatcher(Array(0))).toBe(false);
@@ -86,19 +83,19 @@ describe("createMutateHook", () => {
8683
});
8784

8885
it("matches when prefix and path are equal and init isn't given", async () => {
89-
await mutate(["/pet/findByTags"]);
86+
await mutate(["/pet/findByStatus"]);
9087
const keyMatcher = getKeyMatcher();
9188

9289
// Same path, no init
93-
expect(keyMatcher(["<unique-key>", "/pet/findByTags"])).toBe(true);
90+
expect(keyMatcher(["<unique-key>", "/pet/findByStatus"])).toBe(true);
9491

9592
// Same path, init ignored
9693
expect(
97-
keyMatcher(["<unique-key>", "/pet/findByTags", { some: "init" }]),
94+
keyMatcher(["<unique-key>", "/pet/findByStatus", { some: "init" }]),
9895
).toBe(true);
9996

10097
// Same path, undefined init ignored
101-
expect(keyMatcher(["<unique-key>", "/pet/findByTags", undefined])).toBe(
98+
expect(keyMatcher(["<unique-key>", "/pet/findByStatus", undefined])).toBe(
10299
true,
103100
);
104101
});
@@ -107,7 +104,7 @@ describe("createMutateHook", () => {
107104
const psudeoCompare = vi.fn().mockReturnValue("booleanPlaceholder");
108105

109106
const prefix = "<unique-key>";
110-
const path = "/pet/findByTags";
107+
const path = "/pet/findByStatus";
111108
const givenInit = {};
112109

113110
const useMutate = createMutateHook(client, prefix, psudeoCompare);

src/__test__/query-base.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ describe("configureBaseQueryHook", () => {
3030

3131
it("passes correct key to useSWR", () => {
3232
useQuery("/store/inventory", {});
33+
3334
expect(useSWR).toHaveBeenLastCalledWith(
3435
["<unique-key>", "/store/inventory", {}],
3536
expect.any(Function),
37+
undefined,
3638
);
3739

3840
useQuery("/pet/findByTags", {
@@ -42,6 +44,7 @@ describe("configureBaseQueryHook", () => {
4244
},
4345
},
4446
});
47+
4548
expect(useSWR).toHaveBeenLastCalledWith(
4649
[
4750
"<unique-key>",
@@ -55,19 +58,21 @@ describe("configureBaseQueryHook", () => {
5558
},
5659
],
5760
expect.any(Function),
61+
undefined,
5862
);
5963

60-
// @ts-expect-error - TODO: Support undefined init when not required
6164
useQuery("/store/inventory");
65+
6266
expect(useSWR).toHaveBeenLastCalledWith(
6367
["<unique-key>", "/store/inventory", undefined],
6468
expect.any(Function),
69+
undefined,
6570
);
6671
});
6772

6873
it("passes correct fetcher to useSWR", async () => {
6974
// Note: useQuery input doesn't matter here, since we test the fetcher in isolation
70-
useQuery("/pet/findByTags", {});
75+
useQuery("/pet/findByStatus");
7176

7277
const fetcher = useSWR.mock.lastCall![1];
7378

@@ -89,14 +94,14 @@ describe("configureBaseQueryHook", () => {
8994

9095
await expect(() =>
9196
fetcher!(["some-key", "any-path", { some: "init" }]),
92-
).rejects.toThrowError(new Error("Yikes"));
97+
).rejects.toThrow(new Error("Yikes"));
9398
});
9499

95100
it("passes correct config to useSWR", () => {
96-
useQuery("/pet/findByTags", {}, { errorRetryCount: 56 });
101+
useQuery("/pet/findByStatus", {}, { errorRetryCount: 56 });
97102

98103
expect(useSWR).toHaveBeenLastCalledWith(
99-
["<unique-key>", "/pet/findByTags", {}],
104+
["<unique-key>", "/pet/findByStatus", {}],
100105
expect.any(Function),
101106
{ errorRetryCount: 56 },
102107
);

0 commit comments

Comments
 (0)