From fdde4c8c2d5895a52a0fc189221da99d24eb017f Mon Sep 17 00:00:00 2001 From: Lucas Champagne <31268463+Carbonateb@users.noreply.github.com> Date: Sat, 9 Nov 2024 14:22:06 +1000 Subject: [PATCH 1/5] Write return type to generated api spec --- bun.lockb | Bin 32401 -> 99395 bytes package.json | 1 + src/vite/helpers.ts | 94 ++++++++++++++++++++++++++++++++++---------- src/vite/plugin.ts | 16 +++++--- 4 files changed, 84 insertions(+), 27 deletions(-) diff --git a/bun.lockb b/bun.lockb index f6dcbf0444263bee2255d72771d0c27f49530946..c8355331b177a65d464ccf6f36befd5e73e89d19 100755 GIT binary patch literal 99395 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 delta 7976 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 { return `{ ${entries.map(([name, type]) => `${name}: ${type}`).join("; ")} }`; } -export function getSchemaFromFunction(func: ts.SignatureDeclaration) { +export function getSchemaFromFunction(typeChecker: ts.TypeChecker, func: ts.SignatureDeclaration) { + return { + schema: getSchema(func), + returnType: getReturnType(typeChecker, func), + }; +} + +function getSchema(func: ts.SignatureDeclaration) { return ts.forEachChild(func, (node) => { if (ts.isBlock(node)) { return ts.forEachChild(node, (node) => { @@ -28,31 +35,71 @@ export function getSchemaFromFunction(func: ts.SignatureDeclaration) { }); } +function getReturnType(typeChecker: ts.TypeChecker, func: ts.SignatureDeclaration) { + let returnType: ts.Type | undefined; + + ts.forEachChild(func, (node) => { + if (ts.isBlock(node)) { + ts.forEachChild(node, (child) => { + if (ts.isReturnStatement(child) && child.expression) { + if (ts.isCallExpression(child.expression)) { + const calledFunction = child.expression.expression; + const calledFunctionText = calledFunction.getText(); + + if (calledFunctionText === "json") { + const jsonArgument = child.expression.arguments[0]; + if (jsonArgument) { + returnType = typeChecker.getTypeAtLocation(jsonArgument); + // TODO: Apply 'as const' effect to returnType (don't know how to) + // Workaround is to just write "as const" in your code + } + } + } + + // If not a 'json' call, use any (would use unknown but that is not available) + if (!returnType) { + returnType = typeChecker.getAnyType(); + } + } + }); + } + }); + + return returnType ?? typeChecker.getVoidType(); +} + +/** + * Turns a schema (obtained via `getSchemaFromFunction`) and parses it into strings `body` and `searchParams`. + * @param data The schema from getSchemaFromFunction + * @param skipBody Won't output body, set this to True on methods that don't support a body (like GET) + * @returns Body and SearchParams, which are string representations of the types + */ export function parseSchema( typeChecker: ts.TypeChecker, - zodVariableDeclaration: ts.Node, + data: ReturnType, skipBody = false ) { - const zodInputType = typeChecker.getTypeAtLocation(zodVariableDeclaration); - - const body = skipBody - ? undefined - : `{ ${typeChecker - .getPropertiesOfType(zodInputType) - .map((property) => { - if (property.getName() === "searchParams") return null; - const outputType = getZodTypeToString( - typeChecker, - typeChecker.getTypeOfSymbol(property) - ); - if (!outputType) return null; - return `${property.getName()}: ${outputType}`; - }) - .filter((t) => t !== null) - .join("; ")} }`; + const zodInputType = data.schema ? typeChecker.getTypeAtLocation(data.schema) : undefined; + + const body = + skipBody || !zodInputType + ? undefined + : `{ ${typeChecker + .getPropertiesOfType(zodInputType) + .map((property) => { + if (property.getName() === "searchParams") return null; + const outputType = getZodTypeToString( + typeChecker, + typeChecker.getTypeOfSymbol(property) + ); + if (!outputType) return null; + return `${property.getName()}: ${outputType}`; + }) + .filter((t) => t !== null) + .join("; ")} }`; let searchParams: string | undefined; - const searchParamsSymbol = zodInputType.getProperty("searchParams"); + const searchParamsSymbol = zodInputType?.getProperty("searchParams"); if (searchParamsSymbol) { const searchParamsType = typeChecker.getTypeOfSymbol(searchParamsSymbol); @@ -62,7 +109,12 @@ export function parseSchema( } } - return { body: body != "{ }" ? body : undefined, searchParams }; + let returnType: string | undefined; + if (data.returnType) { + returnType = typeChecker.typeToString(data.returnType); + } + + return { body: body != "{ }" ? body : undefined, searchParams, returnType }; } function getZodTypeToString(typeChecker: ts.TypeChecker, type: ts.Type): string { diff --git a/src/vite/plugin.ts b/src/vite/plugin.ts index 1610fe2..e950524 100644 --- a/src/vite/plugin.ts +++ b/src/vite/plugin.ts @@ -60,7 +60,8 @@ export function typesafeApi(): Plugin { function parseFile(file: ts.SourceFile, typeChecker: ts.TypeChecker) { const endpointsFound: Method[] = []; const allBodies: EndpointData = {}; - let allSearchParams: EndpointData = {}; + const allSearchParams: EndpointData = {}; + const allReturnTypes: EndpointData = {}; ts.forEachChild(file, (node) => { if ( @@ -83,22 +84,23 @@ export function typesafeApi(): Plugin { if (ts.isSatisfiesExpression(node)) { return ts.forEachChild(node.expression, (node) => { if (ts.isFunctionLike(node)) { - return getSchemaFromFunction(node); + return getSchemaFromFunction(typeChecker, node); } }); } else if (ts.isFunctionLike(node)) { - return getSchemaFromFunction(node); + return getSchemaFromFunction(typeChecker, node); } }); if (schema) { - const { body, searchParams } = parseSchema( + const { body, searchParams, returnType } = parseSchema( typeChecker, schema, variableName.toUpperCase() === "GET" ); allBodies[variableName as Method] = body; allSearchParams[variableName as Method] = searchParams; + allReturnTypes[variableName as Method] = returnType; } } }); @@ -110,15 +112,16 @@ export function typesafeApi(): Plugin { if (methods.includes(variableName)) { endpointsFound.push(variableName as Method); - const schema = getSchemaFromFunction(node); + const schema = getSchemaFromFunction(typeChecker, node); if (schema) { - const { body, searchParams } = parseSchema( + const { body, searchParams, returnType } = parseSchema( typeChecker, schema, variableName.toUpperCase() === "GET" ); allBodies[variableName as Method] = body; allSearchParams[variableName as Method] = searchParams; + allReturnTypes[variableName as Method] = returnType; } } } @@ -154,6 +157,7 @@ export function typesafeApi(): Plugin { body: allBodies[method], routeParams: routeParams, searchParams: allSearchParams[method], + returns: allReturnTypes[method], }); } } From 0a3dc1224d1274000f58c8df6420b5064bafeece Mon Sep 17 00:00:00 2001 From: Lucas Champagne <31268463+Carbonateb@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:25:32 +1000 Subject: [PATCH 2/5] Introduce new RPC object --- src/fetch.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/index.ts | 3 ++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/fetch.ts b/src/fetch.ts index d230b03..8c0663c 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -26,7 +26,7 @@ const apiFetch = function >( export function createApiObject(fetch: Fetch) { function createRequest(method: M) { - // MM is required to make sure that TypeScript doens't replace the AllowedUrl with never + // MM is required to make sure that TypeScript doesn't replace the AllowedUrl with never return >(url: U, init: PartialInit) => apiFetch(method, url, init, fetch); } @@ -41,6 +41,54 @@ export function createApiObject(fetch: Fetch) { }; } +const rpcFetch = async function >( + method: M, + url: U, + init: Init, + fetch: Fetch +): Promise> { + const body = init.body ? JSON.stringify((init as RequestInit).body) : undefined; + const routeParams = init.routeParams ? Object.entries(init.routeParams) : []; + const searchParams = init.searchParams ? Object.entries(init.searchParams) : []; + + const headers = { + Accept: "application/json", + ...(body ? { "Content-Type": "application/json" } : {}), + ...(init ? init.headers : {}), + }; + + delete init.routeParams; + delete init.searchParams; + + const result = await fetch(generateUrl(url as string, routeParams, searchParams), { + ...init, + method, + headers, + body, + }); + + return await result.json(); +}; + +export function createRpcObject(fetch: Fetch) { + function createRequest(method: M) { + // MM is required to make sure that TypeScript doesn't replace the AllowedUrl with never + return >( + url: U, + init: PartialInit + ): Promise> => rpcFetch(method as MM, url, init, fetch); + } + + return { + GET: createRequest("GET"), + POST: createRequest("POST"), + PATCH: createRequest("PATCH"), + PUT: createRequest("PUT"), + DELETE: createRequest("DELETE"), + OPTIONS: createRequest("OPTIONS"), + }; +} + function generateUrl( route: string, routeParams: [string, string][], @@ -92,6 +140,11 @@ type AllowedData< Field extends string, > = Field extends keyof ProjectAPI[M][U] ? ProjectAPI[M][U][Field] : never; +type ApiReturnType< + M extends AllowedMethod, + U extends AllowedUrl, +> = "returns" extends keyof ProjectAPI[M][U] ? ProjectAPI[M][U]["returns"] : never; + type Init> = Omit< RequestInit, "body" | "method" diff --git a/src/index.ts b/src/index.ts index 8fdd0a6..1868b09 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ -import { createApiObject } from "./fetch.js"; +import { createApiObject, createRpcObject } from "./fetch.js"; export const api = createApiObject(fetch); +export const rpc = createRpcObject(fetch); From f584b3ba3f6a3df54f55026c386281df4bd74626 Mon Sep 17 00:00:00 2001 From: Lucas Champagne <31268463+Carbonateb@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:05:55 +1000 Subject: [PATCH 3/5] Throw error if response status is not 200 --- src/fetch.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fetch.ts b/src/fetch.ts index 8c0663c..1ae64d3 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -67,6 +67,12 @@ const rpcFetch = async function Date: Sat, 9 Nov 2024 16:10:08 +1000 Subject: [PATCH 4/5] Made response available when throwing in rpc --- src/fetch.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/fetch.ts b/src/fetch.ts index 1ae64d3..d1f7af4 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -41,6 +41,14 @@ export function createApiObject(fetch: Fetch) { }; } +class RpcFetchError extends Error { + response: Response; + constructor(response: Response) { + super(`rpcFetch failed: Received status ${response.status}. Data: '${response.text()}'`); + this.response = response; + } +} + const rpcFetch = async function >( method: M, url: U, @@ -68,9 +76,7 @@ const rpcFetch = async function Date: Sat, 9 Nov 2024 16:36:19 +1000 Subject: [PATCH 5/5] Export RpcFetchError --- src/fetch.ts | 2 +- src/index.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fetch.ts b/src/fetch.ts index d1f7af4..5bddeb0 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -41,7 +41,7 @@ export function createApiObject(fetch: Fetch) { }; } -class RpcFetchError extends Error { +export class RpcFetchError extends Error { response: Response; constructor(response: Response) { super(`rpcFetch failed: Received status ${response.status}. Data: '${response.text()}'`); diff --git a/src/index.ts b/src/index.ts index 1868b09..1dd7b54 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ import { createApiObject, createRpcObject } from "./fetch.js"; +export { RpcFetchError } from "./fetch.js"; + export const api = createApiObject(fetch); export const rpc = createRpcObject(fetch);