feat(kit,nuxt): augment nitro types with addTypeTemplate (#31079)

This commit is contained in:
Daniel Roe 2025-03-01 08:11:23 +00:00
parent d153ea9d23
commit 156ab7c93c
No known key found for this signature in database
GPG Key ID: CBC814C393D93268
6 changed files with 69 additions and 10 deletions

View File

@ -48,8 +48,12 @@ export function addServerTemplate (template: NuxtServerTemplate) {
/** /**
* Renders given types during build to disk in the project `buildDir` * Renders given types during build to disk in the project `buildDir`
* and register them as types. * and register them as types.
*
* You can pass a second context object to specify in which context the type should be added.
*
* If no context object is passed, then it will only be added to the nuxt context.
*/ */
export function addTypeTemplate<T> (_template: NuxtTypeTemplate<T>) { export function addTypeTemplate<T> (_template: NuxtTypeTemplate<T>, context?: { nitro?: boolean, nuxt?: boolean }) {
const nuxt = useNuxt() const nuxt = useNuxt()
const template = addTemplate(_template) const template = addTemplate(_template)
@ -59,9 +63,16 @@ export function addTypeTemplate<T> (_template: NuxtTypeTemplate<T>) {
} }
// Add template to types reference // Add template to types reference
nuxt.hook('prepare:types', ({ references }) => { if (!context || context.nuxt) {
references.push({ path: template.dst }) nuxt.hook('prepare:types', ({ references }) => {
}) references.push({ path: template.dst })
})
}
if (context?.nitro) {
nuxt.hook('nitro:prepare:types', ({ references }) => {
references.push({ path: template.dst })
})
}
return template return template
} }

View File

@ -1,13 +1,13 @@
import { existsSync } from 'node:fs' import { existsSync } from 'node:fs'
import { genArrayFromRaw, genDynamicImport, genExport, genImport, genObjectFromRawEntries, genSafeVariableName, genString } from 'knitwork' import { genArrayFromRaw, genDynamicImport, genExport, genImport, genObjectFromRawEntries, genSafeVariableName, genString } from 'knitwork'
import { join, relative, resolve } from 'pathe' import { isAbsolute, join, relative, resolve } from 'pathe'
import type { JSValue } from 'untyped' import type { JSValue } from 'untyped'
import { generateTypes, resolveSchema } from 'untyped' import { generateTypes, resolveSchema } from 'untyped'
import escapeRE from 'escape-string-regexp' import escapeRE from 'escape-string-regexp'
import { hash } from 'ohash' import { hash } from 'ohash'
import { camelCase } from 'scule' import { camelCase } from 'scule'
import { filename } from 'pathe/utils' import { filename } from 'pathe/utils'
import type { NuxtOptions, NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema' import type { NuxtOptions, NuxtTemplate, NuxtTypeTemplate, TSReference } from 'nuxt/schema'
import type { Nitro } from 'nitropack' import type { Nitro } from 'nitropack'
import { annotatePlugins, checkForCircularDependencies } from './app' import { annotatePlugins, checkForCircularDependencies } from './app'
@ -331,10 +331,38 @@ export const middlewareTemplate: NuxtTemplate = {
}, },
} }
function renderAttr (key: string, value?: string) {
return value ? `${key}="${value}"` : ''
}
function renderAttrs (obj: Record<string, string>) {
const attrs: string[] = []
for (const key in obj) {
attrs.push(renderAttr(key, obj[key]))
}
return attrs.join(' ')
}
export const nitroSchemaTemplate: NuxtTemplate = { export const nitroSchemaTemplate: NuxtTemplate = {
filename: 'types/nitro-nuxt.d.ts', filename: 'types/nitro-nuxt.d.ts',
getContents () { async getContents ({ nuxt }) {
const references = [] as TSReference[]
const declarations = [] as string[]
await nuxt.callHook('nitro:prepare:types', { references, declarations })
const sourceDir = join(nuxt.options.buildDir, 'types')
const lines = [
...references.map((ref) => {
if ('path' in ref && isAbsolute(ref.path)) {
ref.path = relative(sourceDir, ref.path)
}
return `/// <reference ${renderAttrs(ref)} />`
}),
...declarations,
]
return /* typescript */` return /* typescript */`
${lines.join('\n')}
/// <reference path="./schema.d.ts" /> /// <reference path="./schema.d.ts" />
import type { RuntimeConfig } from 'nuxt/schema' import type { RuntimeConfig } from 'nuxt/schema'

View File

@ -158,7 +158,7 @@ export default defineNuxtModule({
'}', '}',
'export {}', 'export {}',
].join('\n'), ].join('\n'),
}) }, { nuxt: true, nitro: true })
addComponent({ addComponent({
name: 'NuxtPage', name: 'NuxtPage',
priority: 10, // built-in that we do not expect the user to override priority: 10, // built-in that we do not expect the user to override
@ -575,6 +575,16 @@ export default defineNuxtModule({
' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>', ' middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>',
' }', ' }',
'}', '}',
].join('\n')
},
})
addTypeTemplate({
filename: 'types/nitro-middleware.d.ts',
getContents: ({ app }) => {
const namedMiddleware = app.middleware.filter(mw => !mw.global)
return [
`export type MiddlewareKey = ${namedMiddleware.map(mw => genString(mw.name)).join(' | ') || 'never'}`,
'declare module \'nitropack\' {', 'declare module \'nitropack\' {',
' interface NitroRouteConfig {', ' interface NitroRouteConfig {',
' appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>', ' appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>',
@ -582,7 +592,7 @@ export default defineNuxtModule({
'}', '}',
].join('\n') ].join('\n')
}, },
}) }, { nuxt: true, nitro: true })
addTypeTemplate({ addTypeTemplate({
filename: 'types/layouts.d.ts', filename: 'types/layouts.d.ts',

View File

@ -257,6 +257,12 @@ export interface NuxtHooks {
'components:extend': (components: Component[]) => HookResult 'components:extend': (components: Component[]) => HookResult
// Nitropack // Nitropack
/**
* Called before Nitro writes `.nuxt/tsconfig.server.json`, allowing addition of custom references and declarations.
* @param options Objects containing `references`, `declarations`
* @returns Promise
*/
'nitro:prepare:types': (options: { references: TSReference[], declarations: string[] }) => HookResult
/** /**
* Called before initializing Nitro, allowing customization of Nitro's configuration. * Called before initializing Nitro, allowing customization of Nitro's configuration.
* @param nitroConfig The nitro config to be extended * @param nitroConfig The nitro config to be extended

View File

@ -10,7 +10,7 @@ export default defineNuxtConfig({
addTypeTemplate({ addTypeTemplate({
filename: 'test.d.ts', filename: 'test.d.ts',
getContents: () => 'declare type Fromage = "cheese"', getContents: () => 'declare type Fromage = "cheese"',
}) }, { nuxt: true, nitro: true })
function _test () { function _test () {
installModule('~/modules/example', { installModule('~/modules/example', {
typeTest (val) { typeTest (val) {

View File

@ -0,0 +1,4 @@
// @ts-expect-error Fromage is 'cheese'
const _fake: Fromage = 'babybel'
const _fromage: Fromage = 'cheese'