Skip to content

Commit

Permalink
feat(nitro): automatically type middleware/api routes (#708)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored Oct 11, 2021
1 parent a068263 commit 3163fa2
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 15 deletions.
32 changes: 31 additions & 1 deletion src/build.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolve, join } from 'pathe'
import { relative, resolve, join } from 'pathe'
import consola from 'consola'
import { rollup, watch as rollupWatch } from 'rollup'
import fse from 'fs-extra'
Expand Down Expand Up @@ -55,9 +55,38 @@ export async function build (nitroContext: NitroContext) {

nitroContext.rollupConfig = getRollupConfig(nitroContext)
await nitroContext._internal.hooks.callHook('nitro:rollup:before', nitroContext)
await writeTypes(nitroContext)
return nitroContext._nuxt.dev ? _watch(nitroContext) : _build(nitroContext)
}

async function writeTypes (nitroContext: NitroContext) {
const routeTypes: Record<string, string[]> = {}

const middleware = [
...nitroContext.scannedMiddleware,
...nitroContext.middleware
]

for (const mw of middleware) {
if (typeof mw.handle !== 'string') { continue }
const relativePath = relative(nitroContext._nuxt.buildDir, mw.handle).replace(/\.[a-z]+$/, '')
routeTypes[mw.route] = routeTypes[mw.route] || []
routeTypes[mw.route].push(`ReturnType<typeof import('${relativePath}').default>`)
}

const lines = [
'declare module \'@nuxt/nitro\' {',
' interface InternalApi {',
...Object.entries(routeTypes).map(([path, types]) => ` '${path}': ${types.join(' | ')}`),
' }',
'}',
// Makes this a module for augmentation purposes
'export {}'
]

await writeFile(join(nitroContext._nuxt.buildDir, 'nitro.d.ts'), lines.join('\n'))
}

async function _build (nitroContext: NitroContext) {
nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir)

Expand Down Expand Up @@ -117,6 +146,7 @@ async function _watch (nitroContext: NitroContext) {
nitroContext.scannedMiddleware = middleware
if (['add', 'addDir'].includes(event)) {
watcher.close()
writeTypes(nitroContext).catch(console.error)
watcher = startRollupWatcher(nitroContext)
}
}
Expand Down
41 changes: 41 additions & 0 deletions src/types/fetch.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { FetchRequest, FetchOptions, FetchResponse } from 'ohmyfetch'

// An interface to extend in a local project
export declare interface InternalApi { }

export declare type ValueOf<C> = C extends Record<any, any> ? C[keyof C] : never

export declare type MatchedRoutes<Route extends string> = ValueOf<{
// exact match, prefix match or root middleware
[key in keyof InternalApi]: Route extends key | `${key}/${string}` | '/' ? key : never
}>

export declare type MiddlewareOf<Route extends string> = Exclude<InternalApi[MatchedRoutes<Route>], Error | void>

export declare type TypedInternalResponse<Route, Default> =
Default extends string | boolean | number | null | void | object
// Allow user overrides
? Default
: Route extends string
? MiddlewareOf<Route> extends never
// Bail if only types are Error or void (for example, from middleware)
? Default
: MiddlewareOf<Route>
: Default

export declare interface $Fetch {
<T = unknown, R extends FetchRequest = FetchRequest> (request: R, opts?: FetchOptions): Promise<TypedInternalResponse<R, T>>
raw<T = unknown, R extends FetchRequest = FetchRequest> (request: R, opts?: FetchOptions): Promise<FetchResponse<TypedInternalResponse<R, T>>>
}

declare global {
// eslint-disable-next-line no-var
var $fetch: $Fetch
namespace NodeJS {
interface Global {
$fetch: $Fetch
}
}
}

export default {}
1 change: 1 addition & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ declare module '@nuxt/kit' {
}
}

export * from './fetch'
export * from '../dist'
14 changes: 0 additions & 14 deletions src/types/shims.d.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
declare global {
import type { $Fetch } from 'ohmyfetch'

// eslint-disable-next-line no-var
var $fetch: $Fetch
namespace NodeJS {
interface Global {
$fetch: $Fetch
}
}
}

declare module '#storage' {
import type { Storage } from 'unstorage'
export const storage: Storage
Expand All @@ -21,5 +9,3 @@ declare module '#assets' {
export function statAsset(id: string): Promise<AssetMeta>
export function getKeys() : Promise<string[]>
}

export default {}

0 comments on commit 3163fa2

Please sign in to comment.