mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat(nitro): automatically type middleware/api routes (#708)
This commit is contained in:
parent
18cf0ba865
commit
b005b2403f
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,6 +22,7 @@ dist
|
|||||||
.nuxt*
|
.nuxt*
|
||||||
.output
|
.output
|
||||||
.gen
|
.gen
|
||||||
|
nuxt.d.ts
|
||||||
|
|
||||||
# Junit reports
|
# Junit reports
|
||||||
reports
|
reports
|
||||||
|
@ -123,6 +123,11 @@ export function setupNitroBridge () {
|
|||||||
nitroDevContext.middleware.push(...middleware)
|
nitroDevContext.middleware.push(...middleware)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add typed route responses
|
||||||
|
nuxt.hook('prepare:types', (opts) => {
|
||||||
|
opts.references.push({ path: resolve(nuxt.options.buildDir, 'nitro.d.ts') })
|
||||||
|
})
|
||||||
|
|
||||||
// nuxt build/dev
|
// nuxt build/dev
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nuxt.options.build._minifyServer = false
|
nuxt.options.build._minifyServer = false
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { resolve, join } from 'pathe'
|
import { relative, resolve, join } from 'pathe'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { rollup, watch as rollupWatch } from 'rollup'
|
import { rollup, watch as rollupWatch } from 'rollup'
|
||||||
import fse from 'fs-extra'
|
import fse from 'fs-extra'
|
||||||
@ -55,9 +55,38 @@ export async function build (nitroContext: NitroContext) {
|
|||||||
|
|
||||||
nitroContext.rollupConfig = getRollupConfig(nitroContext)
|
nitroContext.rollupConfig = getRollupConfig(nitroContext)
|
||||||
await nitroContext._internal.hooks.callHook('nitro:rollup:before', nitroContext)
|
await nitroContext._internal.hooks.callHook('nitro:rollup:before', nitroContext)
|
||||||
|
await writeTypes(nitroContext)
|
||||||
return nitroContext._nuxt.dev ? _watch(nitroContext) : _build(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) {
|
async function _build (nitroContext: NitroContext) {
|
||||||
nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir)
|
nitroContext.scannedMiddleware = await scanMiddleware(nitroContext._nuxt.serverDir)
|
||||||
|
|
||||||
@ -117,6 +146,7 @@ async function _watch (nitroContext: NitroContext) {
|
|||||||
nitroContext.scannedMiddleware = middleware
|
nitroContext.scannedMiddleware = middleware
|
||||||
if (['add', 'addDir'].includes(event)) {
|
if (['add', 'addDir'].includes(event)) {
|
||||||
watcher.close()
|
watcher.close()
|
||||||
|
writeTypes(nitroContext).catch(console.error)
|
||||||
watcher = startRollupWatcher(nitroContext)
|
watcher = startRollupWatcher(nitroContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
packages/nitro/types/fetch.d.ts
vendored
Normal file
41
packages/nitro/types/fetch.d.ts
vendored
Normal file
@ -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
packages/nitro/types/index.d.ts
vendored
1
packages/nitro/types/index.d.ts
vendored
@ -7,4 +7,5 @@ declare module '@nuxt/kit' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from './fetch'
|
||||||
export * from '../dist'
|
export * from '../dist'
|
||||||
|
14
packages/nitro/types/shims.d.ts
vendored
14
packages/nitro/types/shims.d.ts
vendored
@ -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' {
|
declare module '#storage' {
|
||||||
import type { Storage } from 'unstorage'
|
import type { Storage } from 'unstorage'
|
||||||
export const storage: Storage
|
export const storage: Storage
|
||||||
@ -21,5 +9,3 @@ declare module '#assets' {
|
|||||||
export function statAsset(id: string): Promise<AssetMeta>
|
export function statAsset(id: string): Promise<AssetMeta>
|
||||||
export function getKeys() : Promise<string[]>
|
export function getKeys() : Promise<string[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {}
|
|
||||||
|
7
packages/nuxt3/src/app/types/shims.d.ts
vendored
7
packages/nuxt3/src/app/types/shims.d.ts
vendored
@ -1,15 +1,8 @@
|
|||||||
import { Component } from '@vue/runtime-core'
|
import { Component } from '@vue/runtime-core'
|
||||||
import { $Fetch } from 'ohmyfetch'
|
|
||||||
import { NuxtApp } from '../nuxt'
|
import { NuxtApp } from '../nuxt'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line no-var
|
|
||||||
var $fetch: $Fetch
|
|
||||||
|
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
interface Global {
|
|
||||||
$fetch: $Fetch
|
|
||||||
}
|
|
||||||
interface Process {
|
interface Process {
|
||||||
browser: boolean
|
browser: boolean
|
||||||
client: boolean
|
client: boolean
|
||||||
|
@ -26,6 +26,11 @@ export function initNitro (nuxt: Nuxt) {
|
|||||||
nuxt.hook('close', () => nitroDevContext._internal.hooks.callHook('close'))
|
nuxt.hook('close', () => nitroDevContext._internal.hooks.callHook('close'))
|
||||||
nitroDevContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
nitroDevContext._internal.hooks.hook('nitro:document', template => nuxt.callHook('nitro:document', template))
|
||||||
|
|
||||||
|
// Add typed route responses
|
||||||
|
nuxt.hook('prepare:types', (opts) => {
|
||||||
|
opts.references.push({ path: resolve(nuxt.options.buildDir, 'nitro.d.ts') })
|
||||||
|
})
|
||||||
|
|
||||||
// Add nitro client plugin (to inject $fetch helper)
|
// Add nitro client plugin (to inject $fetch helper)
|
||||||
nuxt.hook('app:resolve', (app) => {
|
nuxt.hook('app:resolve', (app) => {
|
||||||
app.plugins.push({ src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs') })
|
app.plugins.push({ src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs') })
|
||||||
|
7
playground/nuxt.d.ts
vendored
7
playground/nuxt.d.ts
vendored
@ -1,7 +0,0 @@
|
|||||||
// This file is auto generated by `nuxt prepare`
|
|
||||||
// Please do not manually modify this file.
|
|
||||||
|
|
||||||
/// <reference types="nuxt3" />
|
|
||||||
/// <reference path=".nuxt/components.d.ts" />
|
|
||||||
/// <reference path=".nuxt/auto-imports.d.ts" />
|
|
||||||
export {}
|
|
Loading…
Reference in New Issue
Block a user