Skip to content

Commit

Permalink
Support for endpoint generics (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Refzlund committed Apr 20, 2023
1 parent a950439 commit 0a94ca5
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 14 deletions.
31 changes: 27 additions & 4 deletions src/lib/api-types/api-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,32 @@ export function apiUpdater(
continue

const { alias, importName } = parsePath(resolution, path)
importStatements += `import * as ${alias} from "${importName}"\n`
importStatements += `import * as ${alias} from '${importName}'\n`

// Read file
const contents = fs.readFileSync(path, 'utf8')

const regex = /(GET|HEAD|POST|PUT|DELETE|PATCH|OPTIONS)([\s]*=?[\s]*)</g

const genericMethods = []
let match
while ((match = regex.exec(contents)) !== null) {
const [, method] = match
genericMethods.push(method)
}

const key = fileName.replace(/\.(ts|js)$/g, '')
directory[key] = `Z<typeof ${alias}>`

if (genericMethods.length > 0) {
const omitted = genericMethods.map(method => `'${method}'`).join('|')

// Take a look at `routes/genericEndpoints.ts` to make sense of this bad boy
const type = genericMethods.map(method => `${method}: EP<typeof ${alias}.${method}> extends { body: any } | { query: any } ? <const T extends EP<typeof ${alias}.${method}>>(request: T, fetch?: Fetch) => R<typeof ${alias}.${method}<T>> : <const T extends EP<typeof ${alias}.${method}>>(request?: T, fetch?: Fetch) => R<typeof ${alias}.${method}<T>>`).join(',')

directory[key] =
`Z<Omit<typeof ${alias}, ${omitted}>> & { ${type}`
}
}
}
recursiveLoad(routesDirectory, apiTypes)
Expand All @@ -100,7 +122,7 @@ export function apiUpdater(
apiTypes = fixKeys(apiTypes)

let dirText = JSON.stringify(apiTypes, null, 2)

dirText = dirText
// Transform routes into API types
.replaceAll(/\"\+server\"\: \"Z/g, '} & Z')
Expand All @@ -114,6 +136,7 @@ export function apiUpdater(
return `${p1}$: (${p1}: Slug<typeof ${alias}>) =>`;
})
.replaceAll(/=\w+(?=:|\$)/g, '')
.replaceAll(/"/g, '')

const folder = resolve(resolution, '..')
if (!fs.existsSync(folder))
Expand All @@ -137,9 +160,9 @@ export function apiUpdater(
* when developing sveltekit-zero-api and is an incorrect path.
*/
const z = debugging ?
`import type { Z } from '$dist/z'`
`import type { EP, Fetch, Z, R } from '$dist/z'`
:
`import type { Z } from 'sveltekit-zero-api/z'`
`import type { EP, Fetch, Z, R } from 'sveltekit-zero-api/z'`

const file = (dirText: string, importCode: string) =>
`/* eslint-disable */
Expand Down
2 changes: 1 addition & 1 deletion src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Directory = {
}

const endpoints = [
'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'
'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'
] as const

// TODO: I feel like this could be optimized to not check for the keys,
Expand Down
28 changes: 23 additions & 5 deletions src/routes/(app)/api/fo/+server.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import type { API } from '$dist'
import { querySpread, type API } from '$dist'
import { Ok } from '$dist/http'

interface Get<Q> {
query: Q
interface Get {
query: {
foo: string,
bar: string
}
}

export async function GET<const R extends { query?: any, body?: any }>(event: API<Get<R['query']>>) {
export async function GET<const R extends Get>(event: API<R>) {
const query = querySpread(event)

return Ok({
body: {} as unknown as R
body: query as unknown as R['query']
})
}

Expand All @@ -18,4 +23,17 @@ export async function POST(event: API) {
return Ok({
body: bar
})
}

interface Put {
body: {
foo: string
bar: number
}
}

export async function PUT<const R extends Put>(event: API<R>) {
return Ok({
body: await event.request.json() as unknown as R['body']
})
}
6 changes: 3 additions & 3 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
const res = await api.fo.GET({
query: {
test: 123,
str: 'abc'
foo: 'Pizza is great',
bar: 'Yas!'
}
})
.Ok(res => {
console.log(res.body)
})
})
Expand Down
2 changes: 1 addition & 1 deletion src/routes/test.ts → src/routes/genericEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type GeneratedAPI = {
POST: EP<typeof endpoint.POST> extends { body: any } | { query: any }
? <const T extends EP<typeof endpoint.POST>>(request: T, fetch?: Fetch) => R<typeof endpoint.POST<T>>
: <const T extends EP<typeof endpoint.POST>>(request?: T, fetch?: Fetch) => R<typeof endpoint.POST<T>>
} & Z<Omit<typeof endpoint, 'POST'>>
} & Z<Omit<typeof endpoint, 'POST' | 'PUT'>>
}

const api = {} as GeneratedAPI
Expand Down

0 comments on commit 0a94ca5

Please sign in to comment.